Bloat.py
Challenge
1
2
Can you get the flag?
Run this Python program in the same directory as this encrypted flag.
We are given 2 files:
- bloat.flag.py : a script that asks for a password and gives the flag if it’s ok
- flag.txt.enc : the encrypted flag
bloat.flag.py
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
43
44
45
46
47
48
49
50
51
import sys
a = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ "
def arg133(arg432):
if arg432 == a[71] + a[64] + a[79] + a[79] + a[88] + a[66] + a[71] + a[64] + a[77] + a[66] + a[68]:
return True
else:
print(a[51] + a[71] + a[64] + a[83] + a[94] + a[79] + a[64] + a[82] + a[82] + a[86] + a[78] + a[81] + a[67] + a[
94] + a[72] + a[82] + a[94] + a[72] + a[77] + a[66] + a[78] + a[81] + a[81] + a[68] + a[66] + a[83])
sys.exit(0)
return False
def arg111(arg444):
return arg122(arg444.decode(),
a[81] + a[64] + a[79] + a[82] + a[66] + a[64] + a[75] + a[75] + a[72] + a[78] + a[77])
def arg232():
return input(
a[47] + a[75] + a[68] + a[64] + a[82] + a[68] + a[94] + a[68] + a[77] + a[83] + a[68] + a[81] + a[94] + a[66] +
a[78] + a[81] + a[81] + a[68] + a[66] + a[83] + a[94] + a[79] + a[64] + a[82] + a[82] + a[86] + a[78] + a[81] +
a[67] + a[94] + a[69] + a[78] + a[81] + a[94] + a[69] + a[75] + a[64] + a[70] + a[25] + a[94])
def arg132():
return open('flag.txt.enc', 'rb').read()
def arg112():
print(a[54] + a[68] + a[75] + a[66] + a[78] + a[76] + a[68] + a[94] + a[65] + a[64] + a[66] + a[74] + a[13] + a[13] + a[13] + a[94] + a[88] + a[78] + a[84] + a[81] + a[94] + a[69] + a[75] + a[64] + a[70] + a[11] + a[94] + a[84] + a[82] + a[68] + a[81] + a[25])
def arg122(arg432, arg423):
arg433 = arg423
i = 0
while len(arg433) < len(arg432):
arg433 = arg433 + arg423[i]
i = (i + 1) % len(arg423)
return "".join([chr(ord(arg422) ^ ord(arg442)) for (arg422, arg442) in zip(arg432, arg433)])
arg444 = arg132()
arg432 = arg232()
arg133(arg432)
arg112()
arg423 = arg111(arg444)
print(arg423)
sys.exit(0)
Solution
The script is obviously obfuscated. Modified version that gives the flag without asking for password :
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
43
import sys
a = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ"+"[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ "
def arg133(arg432):
if arg432 == a[71]+a[64]+a[79]+a[79]+a[88]+a[66]+a[71]+a[64]+a[77]+a[66]+a[68]:
return True
else:
print(a[51]+a[71]+a[64]+a[83]+a[94]+a[79]+a[64]+a[82]+a[82]+a[86]+a[78]+\
a[81]+a[67]+a[94]+a[72]+a[82]+a[94]+a[72]+a[77]+a[66]+a[78]+a[81]+\
a[81]+a[68]+a[66]+a[83])
sys.exit(0)
return False
def arg111(arg444):
return arg122(arg444.decode(), a[81]+a[64]+a[79]+a[82]+a[66]+a[64]+a[75]+\
a[75]+a[72]+a[78]+a[77])
def arg232():
return input(a[47]+a[75]+a[68]+a[64]+a[82]+a[68]+a[94]+a[68]+a[77]+a[83]+\
a[68]+a[81]+a[94]+a[66]+a[78]+a[81]+a[81]+a[68]+a[66]+a[83]+\
a[94]+a[79]+a[64]+a[82]+a[82]+a[86]+a[78]+a[81]+a[67]+a[94]+\
a[69]+a[78]+a[81]+a[94]+a[69]+a[75]+a[64]+a[70]+a[25]+a[94])
def arg132():
return open('flag.txt.enc', 'rb').read()
def arg112():
print(a[54]+a[68]+a[75]+a[66]+a[78]+a[76]+a[68]+a[94]+a[65]+a[64]+a[66]+\
a[74]+a[13]+a[13]+a[13]+a[94]+a[88]+a[78]+a[84]+a[81]+a[94]+a[69]+\
a[75]+a[64]+a[70]+a[11]+a[94]+a[84]+a[82]+a[68]+a[81]+a[25])
def arg122(arg432, arg423):
arg433 = arg423
i = 0
while len(arg433) < len(arg432):
arg433 = arg433 + arg423[i]
i = (i + 1) % len(arg423)
return "".join([chr(ord(arg422) ^ ord(arg442)) for (arg422,arg442) in zip(arg432,arg433)])
arg444 = arg132()
arg423 = arg111(arg444)
print(arg423)
sys.exit(0)
Fresh Java
Challenge
1
2
Can you get the flag?
Reverse engineer this Java program.
Solution
We have to decompile the java class to get the flag. I used cfr for the task
Solve script:
1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python
import subprocess
import re
cfrjar = '/opt/cfr/cfr_0_115.jar'
dec = subprocess.run(['java','-jar',cfrjar,'./KeygenMe.class'], capture_output=True).stdout.decode()
chars = re.compile("\'([\w{}])\'").findall(dec)
flag = ''
for c in chars[::-1]:
flag += c
print(flag)
Unpackme
Challenge
1
2
Can you get the flag?
Reverse engineer this binary.
We are given a UPX binary
Solution
- Unpack the binary
1
$ upx -d unpackme-upx
- Load the binary into gdb
1
$ gdb-peda -q ./unpackme-upx
- Disassemble the ‘main’ function
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
gdb-peda$ disass main Dump of assembler code for function main: 0x0000000000401e73 <+0>: endbr64 0x0000000000401e77 <+4>: push rbp 0x0000000000401e78 <+5>: mov rbp,rsp [...] 0x0000000000401ec6 <+83>: mov WORD PTR [rbp-0x14],0x4e 0x0000000000401ecc <+89>: lea rdi,[rip+0xb1131] 0x0000000000401ed3 <+96>: mov eax,0x0 0x0000000000401ed8 <+101>: call 0x410df0 <printf> # <--- First display (What's my favorite number) 0x0000000000401edd <+106>: lea rax,[rbp-0x3c] 0x0000000000401ee1 <+110>: mov rsi,rax 0x0000000000401ee4 <+113>: lea rdi,[rip+0xb1135] 0x0000000000401eeb <+120>: mov eax,0x0 0x0000000000401ef0 <+125>: call 0x410f80 <__isoc99_scanf> # <--- User input 0x0000000000401ef5 <+130>: mov eax,DWORD PTR [rbp-0x3c] 0x0000000000401ef8 <+133>: cmp eax,0xb83cb # <--- Number validation 0x0000000000401efd <+138>: jne 0x401f42 <main+207> 0x0000000000401eff <+140>: lea rax,[rbp-0x30] 0x0000000000401f03 <+144>: mov rsi,rax [...] 0x0000000000401f53 <+224>: mov rcx,QWORD PTR [rbp-0x8] 0x0000000000401f57 <+228>: xor rcx,QWORD PTR fs:0x28 0x0000000000401f60 <+237>: je 0x401f67 <main+244> 0x0000000000401f62 <+239>: call 0x45cdf0 <__stack_chk_fail_local> 0x0000000000401f67 <+244>: leave 0x0000000000401f68 <+245>: ret End of assembler dump.
- Decode number
1 2
gdb-peda$ python print(0xb83cb) 754635
- Run and win
1 2 3 4
gdb-peda$ run Starting program: unpackme-upx What's my favorite number? 754635 picoCTF{up><_m3_f7w_ed7b0850}
Wizardlike
Challenge
1
2
3
4
5
6
7
8
9
10
Do you seek your destiny in these deplorable dungeons?
If so, you may want to look elsewhere. Many have gone before you and honestly,
they've cleared out the place of all monsters, ne'erdowells, bandits
and every other sort of evil foe. The dungeons themselves have seen better
days too. There's a lot of missing floors and key passages blocked off.
You'd have to be a real wizard to make any progress in this sorry excuse for a dungeon!
Download the game.
'w', 'a', 's', 'd' moves your character and 'Q' quits.
You'll need to improvise some wizardly abilities to find the flag in this dungeon crawl.
'.' is floor, '#' are walls, '<' are stairs up to previous level, and '>' are stairs down to next level.
This is a console game where we can move with ‘wasd’ We can’t go through wall obviously and we can move between levels with the doors ‘>’ ‘<’
As we move through the levels, walls and floors are appearing and some are at places we can’t go because of the walls.
Level Examples
- Start of level 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
### #.@.....# #.......# #........ #.......# .# .......# # .......# .......# .......# .......# .......# .......# .......# ....... ......> #######
- After Moving all the way south
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
######### #.......# ......# .. .............. #.......# ............ #........ .# #.......# .# #.......# # #.......# #.......# #.......# #.......# #.......# #.......# #.......# #.......# #.@....># #########
- After walking everywhere
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
######### #.......# ......# ..................... #.......# ............ #........ .# #.......# .# #.......# .# #.......# .# #.......# .# #.......# . #.......# #.......# #.......# #.......# #......@# #......># #########
Solution
For this one we’ll be using IDA Free to reverse engineer and hack the game
- Load the binary in IDA Free. Launch IDA from the command line to be able to interact with the process
1
cat | /opt/idafree-7.7/ida64
- Decompile the main function by pressing F5 after selecting the function
At the very end of the function, we can see the functions that’s responsible for the keypress action for w,a,s,d,Q
In every one of those functions, we can see that the first thing it does is to get a result from another function.
Let’s look into it
Pseudo-code:
Assembly:
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
15AC sub_15AC proc near
15AC var_8 = dword ptr -8
15AC var_4 = dword ptr -4
15AC endbr64
15B0 push rbp
15B1 mov rbp, rsp
15B4 mov [rbp+var_4], edi
15B7 mov [rbp+var_8], esi
15BA cmp [rbp+var_4], 63h <---- check if position is in range
15BE jg loc_1664 <---- jump to 1664 if not
15C4 cmp [rbp+var_8], 63h <---- check if position is in range
15C8 jg loc_1664 <---- jump to 1664 if not
15CE cmp [rbp+var_4], 0 <---- check if position is in range
15D2 js loc_1664 <---- jump to 1664 if not
15D8 cmp [rbp+var_8], 0 <---- check if position is in range
15DC js loc_1664 <---- jump to 1664 if not
15E2 mov eax, [rbp+var_4]
15E5 movsxd rcx, eax
15E8 mov eax, [rbp+var_8]
15EB movsxd rdx, eax
15EE mov rax, rdx
15F1 shl rax, 2
15F5 add rax, rdx
15F8 lea rdx, ds:0[rax*4]
1600 add rax, rdx
1603 shl rax, 2
1607 lea rdx, [rax+rcx]
160B lea rax, byte_1FEA0
1612 add rax, rdx
1615 movzx eax, byte ptr [rax]
1618 cmp al, 23h ; '#' <---- check if position is a wall
161A jz short loc_1656 <---- jump to 1656 if it is
161C mov eax, [rbp+var_4]
161F movsxd rcx, eax
1622 mov eax, [rbp+var_8]
1625 movsxd rdx, eax
1628 mov rax, rdx
162B shl rax, 2
162F add rax, rdx
1632 lea rdx, ds:0[rax*4]
163A add rax, rdx
163D shl rax, 2
1641 lea rdx, [rax+rcx]
1645 lea rax, byte_1FEA0
164C add rax, rdx
164F movzx eax, byte ptr [rax]
1652 cmp al, 20h ; ' '
1654 jnz short loc_165D <---- if all if good, jump to 165D
1656
1656 loc_1656:
1656 mov eax, 0 <---- set function return to 0
165B jmp short loc_1669
165D ; ---------------------------------------------------------------------------
165D
165D loc_165D:
165D mov eax, 1 <---- set function return to 1
1662 jmp short loc_1669
1664 ; ---------------------------------------------------------------------------
1664
1664 loc_1664:
1664 mov eax, 0 <---- set function return to 0
1669
1669 loc_1669:
1669 pop rbp
166A retn <---- return result
166A sub_15AC endp
What it does is to check if the cursor will still be in range (100x100) if moving to the next position and return 0 if not. Then it checks if the next position is a wall and return 0 if it is indeed a wall (#) If the cursor is allowed to move, it returns 1
With the graph view, we can see the 3 jump locations and their results:
What we want is this function to always return 1.
- Edit the function assembly
Edit > Patch program > Assemble…
We will insert a jump instruction to 165D at the very beginning of the function to skip all the validations
The hacked function’s assembly now look like this:
The pseudocode:
And the graph:
We should now be able to walk through wall to reveal the flag
Save the binary (Edit > Patch program > Apply patches to input file)
- Run the game and win
Level 1:
Level 2:
Level 3:
Level 4:
Level 5:
Level 6:
Level 7:
Level 8:
Level 9:
Flag: picoCTF{ur_4_w1z4rd_2A05D7A}
Thanks for reading <3
h3x