Content
Tools Used
- python
- gdb-pwndbg
- angr
- IDA Pro
Easy as gdb
Description
Points: 160 points
Tags: picoCTF 2021, Reverse Engineering
AUTHOR: MCKADE
Description:
The flag has got to be checked somewhere… File: brute
Hint 1: https://sourceware.org/gdb/onlinedocs/gdb/Basic-Python.html#Basic-Python
Hint 2: With GDB Python, I can guess wrong flags faster than ever before!
Solution
1
2
$ file brute
brute: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV) dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=7ef7ebc27d61a92e4c699bb95aae99d75a90b87f, stripped
The “file” command indicates that the given file is a 32-bit ELF binary. Load this binary into IDA Pro to display the assembly code and pseudo-code.
Within the function sub_9AF, input is acquired through the fgets function, and the output “Correct!” or “Incorrect.” is printed based on the return value of the sub_8C4 function.
The function sub_8C4 serves as a checker, comparing the characters of the flag one by one with the characters of the hardcoded encrypted string within the binary.
The problem can be solved using both GDB Python and Angr. Here are the scripts for each approach.
GDB script:
Command to run the script :
gdb-pwndbg -n -q -ex “set pagination off” -ex “source sol.py” ./brute
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import string
import gdb
gdb.execute("b *0x5655598E")
flag = ""
ALPHABET = string.ascii_letters + string.digits + "{}_"
def read_reg(reg):
return gdb.parse_and_eval("${}".format(reg))
def write_to_file(input):
with open('input', 'w') as f:
f.write(input)
gdb.execute('run < input')
def check_letter(curr, index):
for i in range(index):
gdb.execute("continue")
al = read_reg('eax')
dl = read_reg('edx')
if(al == dl):
return True
return False
for i in range(30):
for letter in ALPHABET:
print("[!] flag : " + flag)
curr_otp = flag + letter
curr_otp += "0"*(30-len(curr_otp))
print("[!] Trying " + curr_otp)
write_to_file(curr_otp)
if check_letter(curr_otp, len(flag)):
flag += letter
print(flag)
angr script:
[!] It took a considerable amount of time to compute the flag.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import angr
import logging
logging.getLogger('angr').setLevel(logging.INFO)
p = angr.Project("brute")
state = p.factory.entry_state()
simgr = p.factory.simgr(state)
x = p.loader.main_object.min_addr
target = x + 0xA6B
avoid = x + 0xA7F
simgr.explore(find = target, avoid = avoid)
if(simgr.found):
print(simgr.found[0].posix.dumps(0))
else:
print("[!] Couldn't find the solution :)")
Flag : picoCTF{I_5D3_A11DA7_61b3a698}
OTP Implementation
Description
Points: 300 points
Tags: picoCTF 2020 Mini-Competition, Reverse Engineering
AUTHOR: MADSTACKS
Yay reversing! Relevant files: otp flag.txt
Hint1: https://sourceware.org/gdb/onlinedocs/gdb/Python-API.html
Hint2: I think GDB Python is very useful, you can solve this problem without it, but can you solve future problems (hint hint)?
Hint3: Also test your skills by solving this with ANGR!
Solution
1
2
$ file otp
otp: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=674d3f4dd902095f3b632572f0c244ca70573f3d, not stripped
The “file” command indicates that it is a 64-bit ELF binary. Upon execution, it prints “USAGE: ./otp [KEY]” and when provided with a string argument, it outputs “Invalid key!”.
To analyze its functions, load it into IDA Pro. In the main function, there is a for loop that iterates through each character. Within this loop, each character is passed to a function named “valid_char” which checks whether the character is in the set “abcdef0123456789” or not.
Next, it calls a function named “jumble” along with some operations that modify the input. Finally, it invokes strncmp to compare the altered input with a hardcoded string.
To solve this, we can use GDB Python by setting a breakpoint at the strncmp function and brute-force the solution by attempting all possible characters, index by index.
GDB script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import string
gdb.execute("set disable-randomization on")
gdb.execute("b *0x5555554009bd")
flag = ""
ALPHABET = "abcdef" + string.digits
target = "occdpnkibjefihcgjanhofnhkdfnabmofnopaghhgnjhbkalgpnpdjonblalfciifiimkaoenpealibelmkdpbdlcldicplephbo"
for j in range(100):
for i in ALPHABET:
curr = "run " + flag + i
curr += "0" * (100 - len(curr) + 4)
gdb.execute(curr)
print("char : " + i + " index : " + str(j))
x = gdb.selected_frame().read_register('rdi').cast(gdb.lookup_type('char').pointer()).string()
print("result : ", x)
if(target[j] == x[j]):
flag += i
break
print("flag : " , flag)
Upon passing these hexadecimal numbers to the binary, it outputs “You got the key, congrats! Now XOR it with the flag!”. Therefore, just need to XOR it with the contents of the flag.txt file.
angr script:
1
Coming Soon!
Flag : picoCTF{cust0m_jumbl3s_4r3nt_4_g0Od_1d3A_15e89ca4}