H1-202 CTF Write-up

On February 16 HackerOne released their new CTF! A chance to win a trip to Washington and the best part: It had some mobile challenges! Since February 12 I started my final year internship where I’ll be focusing on mobile application hacking, so this CTF was very welcome.

I didn’t complete a lot of the previous challenges so I promised myself I would compete in the upcoming CTF. The time has come and below you can read my attempt at solving the H1-202 CTF!

Chapter 1: First flag

flag{easier_th4n_voting_4_pr3z}


Description:

Submit your writeup for the challenge here:
https://goo.gl/forms/SzofqNtkHALDd2A13

The first flag is directly in the app. Can you find it?


Reading the description of the first challenge I thought to myself: Well, this can’t be that hard. I was wrong. I haven’t completed all of the challenges, but I still hope you will enjoy reading my write up of the H1-202 CTF

The first flag was found after decompiling the .apk file with apktool. I opened the freshly decompiled directory with Android Studio, grabbed a cup of coffee and started checking out the source code. As said in the description: “The first flag is directly in the app. Can you find it?”

Of course I can ;)

But it took a quite some coffee to get there. Just a week ago I started with my internship, specializing in mobile hacking. This CTF came as a gift, so many things to learn from this contest! This also contributed to the amount of time I spend on solving the challenges. From learning how to decompile an apk to going through several layers of encryption, this has been a great source of learning in the past week!

Recalling a class I attended at school about Android Development, I suddenly remembered that there should be a values directory containing strings… Could it be that easy?

In the meanwhile I changed to a different decompile tool: from apktool to jadx (https://github.com/skylot/jadx). This made the decompiled output a bit more “human” readable. I had a difficult time reading the smali files and by using the jadx file the files decompiled into .java files.

Enough about the decompile tools, let’s get the first plaintext flag!

Returning to Android Studio: Navigate to the following directory: /res/values/ and open strings.xml. ctrl + f to search: First guess was flag and we got a hit!


<string name ="first_flag">flag{easier_th4n_voting_4_pr3z}</string>

Another method is to use grep:


grep -Hrn 'flag` /home/x1m/tools/jadx/bin/out2/resources/

#Result:

tools/jadx/build/jadx/bin/out2/resources/res/values/strings.xml:45: <string name ="first_flag">flag{easier_th4n_voting_4_pr3z}</string>

flag{easier_th4n_voting_4_pr3z} <- YES! GREAT SUCCESS!

Seeing the flag format was a nice hint and I’ve used that knowledge to discover the encrypted flag

That’s it for the first flag, on to the next!


Chapter 2: Encrypted flag

flag{w0w_i_see_u_can_do_decryption}


Description:

Kate is a high school student from New York City and has been hacking since she was 12 years old.
She enjoys hanging out at hacking nightclub cyberdelia and playing Wipeout the arcade game. 
She is well known for hacking the Gibson and for stopping the computer virus Da Vinci.
She is also known for being a founder of the term, “Hack The Planet!”


The second flag took a lot more time for me to find.

First thing on my mind was to go through the sources/com/hackerone directory and see if anything interesting would stand out. And it did!

Quite some time went by, remember, this is my first week of testing Android applications :)

So after spending a couple of hours in the dark I checked all of the Java classes inside com/hackerone/ and found something interesting:

In the classes the string aaaaaa and variations of that string kept returning. That was the moment when it hit me:

Just like the first flag i used Grep again: grep -Hrn 'aa' /home/x1m/tools/jadx/build/jadx/bin/out2/, giving back a couple of results.


aa("91C6DD1299FD5D1DE9C4A0C78616D244");
aaaa("1E7746CB4B982418E917EDD07F6ACFFA");
aaaaaa("9D2A44020EA764B6AD790A9B1E894BFE");
aaaaaaaaa("DDC09B1C11F8675E0186310A6B36002D");
aaaaaaaaaa("A76CBBE4FA5619E360BF7DFC77D1D49E");
aaaaaaaaaaaa("44648798D358E60D7C4D29B5469CAEA8");
aaaaaaaaaaaaaa("5055DEAA9850A19FB67D4E76BC8FD825");

This was giving me some hope, the values of those strings look like AES encrypted strings!

But as we all know, you need a key to unlock AES encrypted strings. This became my new point of interest.

Going through the com/hackerone directory again I noticed there was one class (f.java) that used the SecretKey function. Interesting.

Looking at this part of the class I noticed that the encryption was set to AES and that it required R.string.title_for_the_current_time


public static SecretKey a(Context context) {
        return new SecretKeySpec(context.getString(R.string.title_for_the_current_time).getBytes(), "AES");
    }

Once again, Grep helped me out a great deal here!


grep -Hrn 'title_for_the_current_time' /home/x1m/tools/jadx/build/jadx/bin/out2/

Resulting in:


/home/x1m/tools/jadx/build/jadx/bin/out2/resources/res/values/strings.xml:58:    <string name="title_for_the_current_time">AAAAAAAAAAAAAAAA</string>
/home/x1m/tools/jadx/build/jadx/bin/out2/sources/com/hackerone/candidatevote/f.java:17:
return new SecretKeySpec(context.getString(R.string.title_for_the_current_time).getBytes(), "AES");

So we now have the encryption key for the AES encryped strings. I threw all of them inside an online decoder, which let to the following results:


aa("91C6DD1299FD5D1DE9C4A0C78616D244");  	
e	l	e	c	t	i	o	n	A	d	m	i	n	:	$	a
aaaa("1E7746CB4B982418E917EDD07F6ACFFA");
S	H	.	.	.	.	.	.	.	.	.	.	.	.	.	.
aaaaaa("9D2A44020EA764B6AD790A9B1E894BFE");
_	c	a	n	_	d	o	_	d	e	c	r	y	p	t	i
aaaaaaaaa("DDC09B1C11F8675E0186310A6B36002D");
 	
f	l	a	g	{	w	0	w	_	i	_	s	e	e	_	u
aaaaaaaaaa("A76CBBE4FA5619E360BF7DFC77D1D49E");
 	
F	x	s	g	8	A	d	0	Q	V	e	m	p	3	s	S
aaaaaaaaaaaa("44648798D358E60D7C4D29B5469CAEA8");
 	
p	r	1	$	Q	k	6	p	n	u	g	W	$	F	x	F
aaaaaaaaaaaaaa("5055DEAA9850A19FB67D4E76BC8FD825");
o	n	}	.	.	.	.	.	.	.	.	.	.	.	.	.

Looks like we got some good results! Knowing in what format the flag should be submitted it was fairly easy to construct a valid flag from the output.

flag{w0w_i_see_u_can_do_decryption}

After validating that it was actually the correct flag I felt pretty content. Mobile challenges done, let’s to the next flag!


Chapter 3: Accessing /admin


Description:

Eliot is a senior network technician at Allsafe Cybersecurity and a vigilante hacker. 
He has social anxiety disorder and deals with clinical depression and delusions, which cause him to struggle socially and live isolated from other people. 
Eliot stays up to date on forums and boards, and maintains contacts through the internet. 
He is skilled in information gathering and observation, and demonstrates skills in social engineering, which allow him to learn as much as possible about the people around him.


First off all: Damn Eliot, you’ve got some baggage with you :/

This is the first challenge I took from the Web part of the CTF. Because the challenge is called “/admin” my first guest was to checkout the /admin endpoint.

While navigating to https://h1-202.h1ctf.com/admin/ I was a bit confused.. A login form was shown and when I entered my h1-202 CTF credentials I was forwarded to the challenge area. What was going on? Well, long story short: I was looking at the wrong base URL :p

After recovering from my terrible mistake I checked the description of the challenge and quickly found the URL http://admin-h1-202.herokuapp.com. Not long after I found the URL, I checked the /admin endpoint and was presented with a popup login form.

This is where the fun starts.

One of the first things that crossed my mind: “Where are those credentials!?”

In this challenge my good old friend Grep dissapointed me.. No credentials or other useful data was given to me.

But then it hit me: In the previous flag I didn’t need all of the AES encrypted strings. Good thing I decrypted them all.

The results that were left behind can be seen below:


aa("91C6DD1299FD5D1DE9C4A0C78616D244");  	
e	l	e	c	t	i	o	n	A	d	m	i	n	:	$	a       electionAdmin:$a
aaaa("1E7746CB4B982418E917EDD07F6ACFFA");
S	H	.	.	.	.	.	.	.	.	.	.	.	.	.	.       SH..............
aaaaaa("9D2A44020EA764B6AD790A9B1E894BFE"); x (ef)
_	c	a	n	_	d	o	_	d	e	c	r	y	p	t	i
aaaaaaaaa("DDC09B1C11F8675E0186310A6B36002D"); x (ef)
 	
f	l	a	g	{	w	0	w	_	i	_	s	e	e	_	u
aaaaaaaaaa("A76CBBE4FA5619E360BF7DFC77D1D49E");
 	
F	x	s	g	8	A	d	0	Q	V	e	m	p	3	s	S       Fxsg8Ad0QVemp3sS
aaaaaaaaaaaa("44648798D358E60D7C4D29B5469CAEA8");
 	
p	r	1	$	Q	k	6	p	n	u	g	W	$	F	x	F       pr1$Qk6pnugW$FxF
aaaaaaaaaaaaaa("5055DEAA9850A19FB67D4E76BC8FD825"); x (ef)
o	n	}	.	.	.	.	.	.	.	.	.	.	.	.	.

So, 4 strings left and (again) it looks like some encrypted strings.

Except for the username, the electionAdmin:$a string kind of reveals the usernames and a little bit of the encryption protocol used.

After some research I found out that this looks like an Apache md5 encryption with salt.

I found this on the hashcat example page: 1600 Apache $apr1$ MD5, md5apr1, MD5 (APR) 2 $apr1$71850310$gh9m4xcAn3MGxogwX/ztb.

The $apr1$ part gave it away for me, so I started building the correct string:

electionAdmin:$a + pr1$Qk6pnugW$FxF + Fxsg8Ad0QVemp3sS + SH..............

Why did I build it up like this? In the Apache MD5 encryption format a username comes first, followed by the $apr1$ part. It should end in a dot "." so that would be the last one to add to the complete build up string.

The complete string: electionAdmin:$apr1$Qk6pnugW$FxFFxsg8Ad0QVemp3sSSH.

String to decrypt: $apr1$Qk6pnugW$FxFFxsg8Ad0QVemp3sSSH.

I checked the hashcat example page again to get the right syntax for the command and came up with the following:


[email protected]:~ hashcat -a 0 -m 1600 ~/hashs.txt /root/SecLists/Passwords/rockyou-75.txt --force

Which gave me the following result:


hashcat (pull/1273/head) starting...

OpenCL Platform #1: The pocl project
====================================
* Device #1: pthread-Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz, 1499/1499 MB allocatable, 4MCU

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Applicable optimizers:
* Zero-Byte
* Single-Hash
* Single-Salt

Watchdog: Hardware monitoring interface not found on your system.
Watchdog: Temperature abort trigger disabled.
Watchdog: Temperature retain trigger disabled.

* Device #1: build_opts '-I /usr/share/hashcat/OpenCL -D VENDOR_ID=64 -D CUDA_ARCH=0 
-D VECT_SIZE=4 -D DEVICE_TYPE=2 -D DGST_R0=0 -D DGST_R1=1 -D DGST_R2=2 -D DGST_R3=3 
-D DGST_ELEM=4 -D KERN_TYPE=1600 -D _unroll -cl-std=CL1.2'
Dictionary cache built:
* Filename..: /root/SecLists/Passwords/rockyou-75.txt
* Passwords.: 59187
* Bytes.....: 478948
* Keyspace..: 59187
* Runtime...: 0 secs

- Device #1: autotuned kernel-accel to 112                
- Device #1: autotuned kernel-loops to 142
$apr1$Qk6pnugW$FxFFxsg8Ad0QVemp3sSSH.:pickles             [s]tatus [p]ause [r]esume [b]ypass [c]heckpoint [q]uit => 
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Type........: Apache $apr1$ MD5, md5apr1, MD5 (APR)
Hash.Target......: $apr1$Qk6pnugW$FxFFxsg8Ad0QVemp3sSSH.
Time.Started.....: Thu Feb 22 08:57:35 2018 (0 secs)
Time.Estimated...: Thu Feb 22 08:57:35 2018 (0 secs)
Guess.Base.......: File (/root/SecLists/Passwords/rockyou-75.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.Dev.#1.....:     3824 H/s (13.59ms)
Recovered........: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts
Progress.........: 1344/59187 (2.27%)
Rejected.........: 0/1344 (0.00%)
Restore.Point....: 896/59187 (1.51%)
Candidates.#1....: sparkle -> teacher
HWMon.Dev.#1.....: N/A

Started: Thu Feb 22 08:57:34 2018
Stopped: Thu Feb 22 08:57:36 2018

If we look into the results the following stands out: $apr1$Qk6pnugW$FxFFxsg8Ad0QVemp3sSSH.:pickles

“pickles” should be the password and electionAdmin the login!

Nice

So now we can return to the web application, which felt for years due to research done for this CTF :p

I quickly navigated to http://admin-h1-202.herokuapp.com/admin and entered credentials, hoping to see the flag after loggin in.

What was I thinking? It couldn’t be THAT simple, right?

Nope.

After loggin in the following error message is shown on http://admin-h1-202.herokuapp.com/admin:

error "Did not provide t query param"

So I gave a “t” query param: http://admin-h1-202.herokuapp.com/admin?t=1

Which resulted in: error "Did not provide name query param"

Again, I gave the “name” query param a value: http://admin-h1-202.herokuapp.com/admin?t=1&name=admin

Which resulted in yet another error: error "Did not provide image query param"

Alright, I’ll give you a value for image too :p


http://admin-h1-202.herokuapp.com/admin?t=1&name=admin&image=https://[redacted].jpg

This time there are no more errors, sweet!

All I have now is a page which is just echoing the t= parameter. I did managage to pop an alert box by giving the t= param the value: <svg onload=confirm(1)>

XSS:


http://admin-h1-202.herokuapp.com/admin?t=%3Csvg%20onload=confirm(1)%3E&name=admin&
image=https://[redacted].jpg

XSS on admin-h1-202

Conclusion

By competing in this CTF I have really challenged myself and learned a ton of new things! It’s too bad I couldn’t finish all of the challenges (respect to you guys who did!) but this week has been great. Maybe I should start watching Mr. Robot, because I think some of the challenges could be more clear if I knew the global theme a bit more.

Thanks for reading and see you at the next CTF!

  • Martijn “x1m”