Skip to content

Commit 25b91a3

Browse files
committed
docs: Add reversing writeups
1 parent 5ffaa5b commit 25b91a3

6 files changed

+349
-0
lines changed

reversing/alien_saboteaur.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Alien saboteaur (medium)
2+
We get a virutal machine and a binary it can execute.
3+
4+
The binary asks us for the keycode. Which I don't know.
5+
6+
Next I opened up ghidra on the `vm` to see how it works.
7+
Basically it loads the specified binary and allocates some space on the heap that will store the binary itself, the program counter and some additional space.
8+
9+
Then the virtual machine is started with `vm_run`, which keeps executing instructions with `vm_step`.
10+
11+
This function will get the op code from the blob and then use that as an index into the instruction lookup table to perform a certain action.
12+
13+
Especially interesting instructions at this point are: `vm_je` and `vm_jne`
14+
After the flag is read it must be that it is compared to some already existing value
15+
16+
Using gdb I have set a breakpoint on `vm_je` and sure enough I saw that my flag values were getting compared.
17+
18+
The keycode length needs to be 17 as that is when execution goes from reading the input to checking it.
19+
20+
However, before comparison got the the end of my flag, I saw that suddenly -1 and 0 were compared, which was odd, since I had neither values in my flag.
21+
22+
After continuing I saw: *Terminal blocked!*
23+
24+
I put 2 and 2 together and thought immediately that there must be some anti debugging technique at play here.
25+
26+
After looking around more I found the `vm_inv` function makes a syscall, so I loaded the binary with `strace`.
27+
And sure enough after entering the passcode `c0d3_r3d_5hAAAAAA` I saw:
28+
```
29+
ptrace(PTRACE_TRACEME) = -1 EPERM (Operation not permitted)
30+
```
31+
So indeed the binary tries to attach a debugger but can't if some other tool is already debugging it.
32+
33+
To get around this problem I simply set a conditional breakpoint on the `vm_je` function for whenever the compared values don't match. When I got to the -1 to 0 comparison again, I just set the values to be equal, and now we are onto the second password.
34+
35+
After experimentation I found out that this password needs to be 36 characters long.
36+
37+
Then similar to the previous password `vm_je` gets invoked, however this time I didn't see my input character anywhere.
38+
39+
After trying an input full of `A`, then `B`, then `C` I have noticed that the value being compared and then one I enter is different, because the entered values get xored by 2.
40+
So now I knew that if I xor the value coming from the binary with 2, that will need to be my input.
41+
42+
The last thing to figure out was that the password was not compared from left to right, but from different positions.
43+
By having an input string with unique characters, I have managed to trakc down which position was being compared to what character.
44+
45+
Finally having replaced all characters I have arrived at the flag:
46+
**HTB{5w1rl_4r0und_7h3_4l13n_l4ngu4g3}**

reversing/cave_system.md

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Cave system (easy)
2+
In this challenge a bunch of condition are checked against the user input.
3+
The user input is the flag itself.
4+
5+
```c
6+
fgets(local_88,0x80,stdin);
7+
iVar1 = memcmp(local_88,&DAT_00102033,4);
8+
if (((((((iVar1 == 0) && ((char)(local_88[21] * local_88[48]) == '\x14')) &&
9+
((char)(local_88[32] - local_88[36]) == -6)) &&
10+
(((((((char)(local_88[37] - local_88[26]) == -0x2a &&
11+
((char)(local_88[16] - local_88[48]) == '\b')) &&
12+
(((char)(local_88[55] - local_88[8]) == -0x2b &&
13+
(((char)(local_88[26] * local_88[7]) == -0x13 &&
14+
((char)(local_88[4] * local_88[24]) == -0x38)))))) &&
15+
((byte)(local_88[34] ^ local_88[28]) == 0x55)) &&
16+
(((((char)(local_88[30] - local_88[55]) == '4' &&
17+
((char)(local_88[59] + local_88[50]) == -0x71)) &&
18+
((char)(local_88[44] + local_88[27]) == -0x2a)) &&
19+
(((byte)(local_88[17] ^ local_88[14]) == 0x31 &&
20+
((char)(local_88[56] * local_88[20]) == -0x54)))))) &&
21+
(((((char)(local_88[58] - local_88[26]) == -0x3e &&
22+
(((byte)(local_88[26] ^ local_88[6]) == 0x2f &&
23+
((byte)(local_88[14] ^ local_88[39]) == 0x5a)))) &&
24+
((byte)(local_88[44] ^ local_88[39]) == 0x40)) &&
25+
(((((local_88[40] == local_88[26] && ((char)(local_88[23] + local_88[49]) == -0x68)) &&
26+
((char)(local_88[23] * local_88[59]) == 'h')) &&
27+
(((char)(local_88[1] - local_88[28]) == -0x25 &&
28+
((char)(local_88[24] - local_88[29]) == -0x2e)))) &&
29+
(((char)(local_88[38] - local_88[24]) == '.' &&
30+
(((byte)(local_88[32] ^ local_88[22]) == 0x1a &&
31+
((char)(local_88[44] * local_88[4]) == -0x60)))))))))))) &&
32+
((((((char)(local_88[38] * local_88[27]) == '^' &&
33+
((((char)(local_88[15] - local_88[40]) == -0x38 &&
34+
((byte)(local_88[49] ^ local_88[53]) == 0x56)) &&
35+
((byte)(local_88[26] ^ local_88[45]) == 0x2b)))) &&
36+
((((((byte)(local_88[54] ^ local_88[9]) == 0x19 &&
37+
((char)(local_88[28] - local_88[47]) == '\x1a')) &&
38+
(((char)(local_88[50] + local_88[19]) == -0x5f &&
39+
(((char)(local_88[37] + local_88[57]) == 'V' &&
40+
((byte)(local_88[29] ^ local_88[18]) == 0x38)))))) &&
41+
((byte)(local_88[44] ^ local_88[60]) == 9)) &&
42+
((((((char)(local_88[15] * local_88[38]) == 'y' &&
43+
((byte)(local_88[37] ^ local_88[30]) == 0x5d)) &&
44+
((char)(local_88[2] * local_88[32]) == '\\')) &&
45+
(((char)(local_88[10] * local_88[18]) == '9' && (local_88[29] == local_88[21])))) &&
46+
(((char)(local_88[35] * local_88[21]) == '/' &&
47+
(((char)(local_88[8] * local_88[37]) == -0x55 &&
48+
((char)(local_88[39] + local_88[26]) == -0x6d)))))))))) &&
49+
(((((((byte)(local_88[26] ^ local_88[34]) == 0x73 &&
50+
((((byte)(local_88[20] ^ local_88[31]) == 0x40 &&
51+
((char)(local_88[25] + local_88[16]) == -0x57)) &&
52+
((byte)(local_88[39] ^ local_88[59]) == 0x15)))) &&
53+
((((char)(local_88[0] + local_88[59]) == 'i' &&
54+
((char)(local_88[34] + local_88[46]) == -0x5b)) &&
55+
(((byte)(local_88[30] ^ local_88[52]) == 0x37 &&
56+
(((char)(local_88[0] * local_88[28]) == '\b' &&
57+
((char)(local_88[34] - local_88[56]) == -0x3b)))))))) &&
58+
((char)(local_88[18] + local_88[60]) == -0x1c)) &&
59+
(((((byte)(local_88[35] ^ local_88[40]) == 0x6e &&
60+
((char)(local_88[56] * local_88[16]) == -0x54)) &&
61+
((char)(local_88[54] - local_88[47]) == '\r')) &&
62+
((((char)(local_88[30] + local_88[55]) == -100 &&
63+
((char)(local_88[6] + local_88[33]) == -0x2c)) &&
64+
(((char)(local_88[7] * local_88[29]) == -0x13 &&
65+
(((byte)(local_88[56] ^ local_88[29]) == 0x38 &&
66+
((char)(local_88[1] * local_88[37]) == 'd')))))))))) &&
67+
(((byte)(local_88[56] ^ local_88[58]) == 0x46 &&
68+
(((((((char)(local_88[2] * local_88[19]) == '&' &&
69+
((byte)(local_88[26] ^ local_88[22]) == 0x2b)) &&
70+
((char)(local_88[1] + local_88[7]) == -0x79)) &&
71+
(((byte)(local_88[27] ^ local_88[0]) == 0x2a &&
72+
((char)(local_88[21] - local_88[1]) == '\v')))) &&
73+
((char)(local_88[27] + local_88[54]) == -0x32)) &&
74+
(((byte)(local_88[17] ^ local_88[13]) == 0x3b &&
75+
((char)(local_88[19] - local_88[58]) == '\x12')))))))))) &&
76+
((((local_88[17] == local_88[10] &&
77+
((((char)(local_88[14] - local_88[58]) == 'M' &&
78+
((char)(local_88[42] * local_88[52]) == 'N')) && (local_88[50] == local_88[32])))) &&
79+
(((byte)(local_88[47] ^ local_88[51]) == 0x38 &&
80+
((char)(local_88[38] + local_88[25]) == -0x6c)))) &&
81+
((char)(local_88[41] + local_88[52]) == -0x31)))))) &&
82+
((((local_88[44] == local_88[20] && ((char)(local_88[12] + local_88[25]) == 'f')) &&
83+
(((char)(local_88[60] + local_88[36]) == -0xf &&
84+
((((char)(local_88[41] - local_88[21]) == '\x11' &&
85+
((char)(local_88[36] - local_88[49]) == 'D')) &&
86+
((char)(local_88[9] - local_88[35]) == 'D')))))) &&
87+
((((byte)(local_88[53] ^ local_88[51]) == 1 && ((byte)(local_88[34] ^ local_88[57]) == 0xd))
88+
&& ((((char)(local_88[11] - local_88[28]) == -0x15 &&
89+
(((((char)(local_88[23] + local_88[24]) == -0x67 &&
90+
((char)(local_88[24] + local_88[13]) == -0x6b)) &&
91+
(((char)(local_88[12] - local_88[0]) == -0x17 &&
92+
(((((char)(local_88[34] + local_88[31]) == '`' &&
93+
((char)(local_88[5] + local_88[53]) == -0x6a)) &&
94+
((char)(local_88[49] * local_88[42]) == '`')) &&
95+
(((char)(local_88[48] * local_88[21]) == '\x14' &&
96+
((char)(local_88[27] - local_88[52]) == '\x03')))))))) &&
97+
((char)(local_88[57] + local_88[20]) == -0x6b)))) &&
98+
((((char)(local_88[10] * local_88[53]) == -0x26 &&
99+
((char)(local_88[1] + local_88[41]) == -0x3c)) &&
100+
(((char)(local_88[47] - local_88[1]) == '\v' &&
101+
(((local_88[43] == local_88[19] && ((char)(local_88[39] + local_88[47]) == -0x6d)) &&
102+
((char)(local_88[12] * local_88[58]) == 'Q')))))))))))))) &&
103+
(((((char)(local_88[8] * local_88[26]) == 'A' && ((char)(local_88[46] - local_88[31]) == 'E'))
104+
&& ((char)(local_88[7] + local_88[37]) == 'h')) &&
105+
(((((char)(local_88[36] + local_88[4]) == -0x44 &&
106+
((char)(local_88[31] + local_88[32]) == -0x5e)) &&
107+
(((char)(local_88[25] + local_88[5]) == 'e' &&
108+
((((char)(local_88[43] * local_88[29]) == -0x13 &&
109+
((byte)(local_88[13] ^ local_88[45]) == 0x10)) &&
110+
((char)(local_88[48] - local_88[12]) == ';')))))) &&
111+
(((((char)(local_88[23] - local_88[8]) == '\t' &&
112+
((byte)(local_88[7] ^ local_88[42]) == 0x41)) &&
113+
((char)(local_88[5] - local_88[43]) == -3)) &&
114+
(((((byte)(local_88[60] ^ local_88[18]) == 0x1a &&
115+
((byte)(local_88[1] ^ local_88[3]) == 0x2f)) &&
116+
(((char)(local_88[17] - local_88[39]) == '+' &&
117+
(((((char)(local_88[8] + local_88[20]) == -0x2d &&
118+
((char)(local_88[11] * local_88[53]) == -0x28)) &&
119+
((char)(local_88[27] + local_88[6]) == -0x2e)) &&
120+
(((char)(local_88[5] + local_88[3]) == -0x55 &&
121+
((char)(local_88[35] - local_88[47]) == -0x2e)))))))) &&
122+
((byte)(local_88[16] ^ local_88[33]) == 0x10)))))))))) {
123+
puts("Freedom at last!");
124+
}
125+
```
126+
127+
Now I promised myself to get more familiar with `angr` and tools as such before the contest, however I didn't have time. Reversing challenges that are solved with symbolic exeuction/SAT solving are somewhat popular I would say, still I had the pleasure of solving this by hand.
128+
129+
Because this is a flag, we know that the first four characters are `HTB{`.
130+
Then we can look for conditions where we know on side of the check, and try to see if there's a singular solution to the condition e.g `<known value> <some opeartion> <unknown value> == <constant>`.
131+
132+
Turns out there are quite some characters for which this is the case. As we start resolving more and more symbols this becomes easier. And finally get the flag.
133+
134+
HTB{H0p3_u_d1dn't_g3t_th15_by_h4nd,1t5_4_pr3tty_l0ng_fl4g!!!}
135+
136+
And the flag even jokes about my solution to the problem. For my next CTF I'll definitely read up on and practice with the intended method.

reversing/hunting_license.md

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Hunting license (easy)
2+
First the binary asks us if we are up for the challenge, and we need to reply with `y`
3+
4+
Then the first password is asked, let's see how to get it:
5+
In ghidra we look into the `exam` function and find
6+
7+
```c
8+
local_10 = (char *)readline(
9+
"Okay, first, a warmup - what\'s the first password? This one\'s not ev en hidden: "
10+
);
11+
iVar1 = strcmp(local_10,"PasswordNumeroUno");
12+
```
13+
14+
Okay, so we know that the first password is *PasswordNumeroUno*
15+
16+
Then for the second password we have the following fragment:
17+
18+
```c
19+
reverse(&local_1c,t,0xb);
20+
local_10 = (char *)readline("Getting harder - what\'s the second password? ");
21+
iVar1 = strcmp(local_10,(char *)&local_1c);
22+
```
23+
24+
First `t` is reversed, then it is checked against the user input.
25+
26+
Therefore we take `t` convert it to ASCII and revers it to get *P4ssw0rdTw0*
27+
28+
For the third password the following code is executed:
29+
```c
30+
xor(&local_38,t2,0x11,0x13);
31+
local_10 = (char *)readline("Your final test - give me the third, and most protected, password: ")
32+
;
33+
iVar1 = strcmp(local_10,(char *)&local_38);
34+
```
35+
36+
Here we see that the `xor` function is called on `t2` and then the result will need to be equal to our password.
37+
38+
```c
39+
void xor(long param_1,long param_2,ulong length,byte param_4)
40+
41+
{
42+
int local_c;
43+
44+
for (local_c = 0; (ulong)(long)local_c < length; local_c = local_c + 1) {
45+
*(byte *)(param_1 + local_c) = *(byte *)(param_2 + local_c) ^ param_4;
46+
}
47+
return;
48+
}
49+
```
50+
51+
An this is the xor function, so we see that `local_38` is the destination and `t2` is the input.
52+
53+
The first `0x11` bytes are going to be xored with `0x13`.
54+
55+
After performing the above operations on `t2` we get *ThirdAndFinal!!!*
56+
57+
Now the actual solution to the challenge is answering some interactive question on a docker instance, so let's do that:
58+
59+
> What is the file format of the executable?
60+
elf
61+
62+
> What is the CPU architecture of the executable?
63+
x86_64
64+
65+
> What library is used to read lines for user answers? (`ldd` may help)
66+
readline
67+
68+
> What is the address of the `main` function?
69+
0x401172
70+
71+
> How many calls to `puts` are there in `main`? (using a decompiler may help)
72+
5
73+
74+
> What is the first password?
75+
PasswordNumeroUno
76+
77+
> What is the reversed form of the second password?
78+
0wTdr0wss4P
79+
80+
> What is the real second password?
81+
P4ssw0rdTw0
82+
83+
> What is the XOR key used to encode the third password?
84+
0x13
85+
86+
> What is the third password?
87+
ThirdAndFinal!!!
88+
89+
> [+] Here is the flag: `HTB{l1c3ns3_4cquir3d-hunt1ng_t1m3!}`

reversing/needle_in_a_haystack.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Needle in a haystack (very easy)
2+
The classic reverse engineering challenge solution.
3+
4+
```shell
5+
➜ rev_needle_haystack strings haystack | grep HTB
6+
HTB{d1v1ng_1nt0_th3_d4tab4nk5}
7+
```

reversing/shattered_tablet.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Shattered tablet (very easy)
2+
I loaded the binary into ghidra and saw a bif `if` statement
3+
4+
```c
5+
printf("Hmmmm... I think the tablet says: ");
6+
fgets(local_48,0x40,stdin);
7+
if (((((((((local_48[31] == 'p') && (local_48[1] == 'T')) && (local_48[7] == 'k')) &&
8+
((local_48[36] == 'd' && (local_48[11] == '4')))) &&
9+
((local_48[20] == 'e' && ((local_48[10] == '_' && (local_48[0] == 'H')))))) &&
10+
(local_48[34] == 'r')) &&
11+
((((local_48[35] == '3' && (local_48[25] == '_')) && (local_48[2] == 'B')) &&
12+
(((local_48[29] == 'r' && (local_48[3] == '{')) &&
13+
((local_48[26] == 'b' && ((local_48[5] == 'r' && (local_48[13] == '4')))))))))) &&
14+
(((local_48[30] == '3' &&
15+
(((local_48[19] == 'v' && (local_48[12] == 'p')) && (local_48[33] == '1')))) &&
16+
(((local_48[27] == '3' && (local_48[17] == 'n')) &&
17+
(((local_48[4] == 'b' && ((local_48[32] == '4' && (local_48[9] == 'n')))) &&
18+
(local_48[16] == ',')))))))) &&
19+
(((((((local_48[8] == '3' && (local_48[6] == '0')) && (local_48[23] == 't')) &&
20+
((local_48[15] == 't' && (local_48[24] == '0')))) &&
21+
((local_48[14] == 'r' && ((local_48[37] == '}' && (local_48[21] == 'r')))))) &&
22+
(local_48[22] == '_')) && ((local_48[18] == '3' && (local_48[28] == '_')))))) {
23+
puts("Yes! That\'s right!");
24+
}
25+
```
26+
27+
Basically check if we have entered the correct flag.
28+
29+
To get the flag I have copied the conditions into a separate python script and replaced `==` with `=`

reversing/she_shells_c_shells.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# She shells C chells (very easy)
2+
I started by loading the binary into ghidra.
3+
4+
The binary mimics a shell and we can use the *help* command to list all valid commands.
5+
6+
I was obviously interested in the *getflag* command, however there was a password requirement.
7+
8+
This command is handled in the `func_flag` function in ghidra.
9+
10+
```c
11+
fgets((char *)&local_118,0x100,stdin);
12+
for (local_c = 0; local_c < 0x4d; local_c = local_c + 1) {
13+
*(byte *)((long)&local_118 + (long)(int)local_c) =
14+
*(byte *)((long)&local_118 + (long)(int)local_c) ^ m1[(int)local_c];
15+
}
16+
local_14 = memcmp(&local_118,t,0x4d);
17+
if (local_14 == 0) {
18+
for (local_10 = 0; local_10 < 0x4d; local_10 = local_10 + 1) {
19+
*(byte *)((long)&local_118 + (long)(int)local_10) =
20+
*(byte *)((long)&local_118 + (long)(int)local_10) ^ m2[(int)local_10];
21+
}
22+
printf("Flag: %s\n",&local_118);
23+
uVar1 = 0;
24+
}
25+
```
26+
27+
So first our input is xored with bytes in `m1`, then we check if the result is same as bytes `t`
28+
Then the flag is retrieved by xoring the result with `m2`.
29+
30+
However since `t` is the result, we can get the flag by xoring `t` with `m2`, both are present in the binary.
31+
32+
```python
33+
t = b'\x2c\x4a\xb7\x99\xa3\xe5\x70\x78\x93\x6e\x97\xd9\x47\x6d\x38\xbd\xff\xbb\x85\x99\x6f\xe1\x4a\xab\x74\xc3\x7b\xa8\xb2\x9f\xd7\xec\xeb\xcd\x63\xb2\x39\x23\xe1\x84\x92\x96\x09\xc6\x99\xf2\x58\xfa\xcb\x6f\x6f\x5e\x1f\xbe\x2b\x13\x8e\xa5\xa9\x99\x93\xab\x8f\x70\x1c\xc0\xc4\x3e\xa6\xfe\x93\x35\x90\xc3\xc9\x10\xe9'
34+
35+
m2 = b'\x64\x1e\xf5\xe2\xc0\x97\x44\x1b\xf8\x5f\xf9\xbe\x18\x5d\x48\x8e\x91\xe4\xf6\xf1\x5c\x8d\x26\x9e\x2b\xa1\x02\xf7\xc6\xf7\xe4\xb3\x98\xfe\x57\xed\x4a\x4b\xd1\xf6\xa1\xeb\x09\xc6\x99\xf2\x58\xfa\xcb\x6f\x6f\x5e\x1f\xbe\x2b\x13\x8e\xa5\xa9\x99\x93\xab\x8f\x70\x1c\xc0\xc4\x3e\xa6\xfe\x93\x35\x90\xc3\xc9\x10\xe9'
36+
37+
l = []
38+
for x in range(0, 0x4d):
39+
l.append(chr(t[x] ^ m2[x]))
40+
41+
print(''.join(l))
42+
```

0 commit comments

Comments
 (0)