NiNi's Den

# 2019::CONFidence

Word count: 3,331Reading time: 21 min
2019/03/17 Share

Teaching CPR during the contest, so I decided to spend the next few day to sovle these challenges without looking writeups.

# Reverse::Elementary

## TL;DR

Solve this with angr is quick and easy

flag:p4{I_really_hope_you_automated_this_somehow_otherwise_it_might_be_a_bit_frustrating_to_do_this_manually}

## details

The binary has lots of trivial functions, some of them return the arg1 directly, the others return arg1^1, this prevents us from parsing the decompile result directly. The angr help us to solve this challenge without knowing anything about those functions.

The other option to solve this is to laverage idapython to parse the decompile result directly, I used to do so.

# Reverse::Old school

## details

The binary ask for 18 hexadecimal digits to draw a graph. Every two of them will be consider as a number, eg: 1a = $26_{10}$. Then, for each number, every two bits of them indicate a way to move on a 2D-array on memory. So, at the end of the funciton sub_10148, the data in 2D-array implys that how much times we walk on to a corresponding position.

What sub_10215 does is replace the number in array to the corresponding character in string p4{krule_ctf}. And S and E in the flag.txt means the start point and the end point. In summary, all we need to do is to find a way to move from S to E and also satify all the numbers marked on each position.

Backtracking is a good choice, but I drew it on paper.

Hexadecimal solution is 50 6b 07 37 07 69 36 67 79 (there should be no space in input)

# Reverse::Pudliszki

## details

This challange gives us a .jar, after decompile, there is a package call kotlin inside it, it’s another language designed for JVM, I’ve never heard of it …, the result of decompiler looks like a mess …. , and there are some weird class like p.class,{.class, }.class. It’s obviously would be use to verify the flag later.

Take a look on main first :

SizeResult localSizeResult = SizeResultFactory.Companion.check($receiver.length, A.class);, this line checks the number of args equals to the length of the name of class A or not. Then, call validateFlag to verify the arg. The decompiled result of validFlag is a mess, need patience to read it, and try to write a couple lines of kotlin. Basically, what validFlag does is : 1. Turn the flag into a list of tuple, eg: String "abab" -> List [(a,0),(b,1),(a,2),(b,3)] 2. Group the list from step 1 according to the first in each tuple, eg: List [(a,0),(b,1),(a,2),(b,3)] -> LinkedHashMap {a=[0,2],b=[1,3]} 3. Call checksum to do some caculation on the LinkedHashMap from step 2 Follow into the function checksum : 1. Call compress to compress the list in LinkedHashMap. LinkedHashMap {a=[0,2],b=[1,3]} -> Collection {(a.class object instance, 0*1+2*32),(b,calss object instance, 1*1+3*32)} 2. Do some caculate according to the first of each tuple and append it to a temp Collection. 3. Apply sum on the Collection and return the result, which should be 0 The caculate in step 2 is : Get the flag by factorizing those number. flag: p4{k0tl1n_1s_p0li5h_ke7chup} # Reverse::Watchmem The binary fork itself by createprocess, then it would try to act like a debugger. When the children return a singal single step or signal illegal instrution, it will decode the real instructions and put it back, and recover it after execution. It’s time comsuming to fully understand the mechanism, I try to analyze the pattern by hook WriteProcessMemory,which can be done by dll injection or debugger script. This is the snippet of result : It turns out that the second records always be the decoded instruction. So, all we need to do is patch it by IDApython Now we can understand the control flow of the encoded binary. Anyway, reverse the operation to get the flag # Web::My admin panel ## TL;DR Set the value of otadmin in cookie to {"hash": 389} flag:p4{wtf_php_comparisons_how_do_they_work} ## source code ## details The result of md5 (128-bit) would be represented as a sequence of 32 hexadecimal digits. So the meaning of hint is that 0 and 64 are the result of ord(digits)&0xc0 and ord(letters)&0xc0 respectively. Then, the $session_data['hash'] != strtoupper(MD5(\$cfg_pass)) is the key point, this is loose comparison. All we need to do is brute force the first three digits to pass this check.

flag
p4{wtf_php_comparisons_how_do_they_work}

# Crypto::Count me in!

## TL;DR

There is a bug inside the worker_function due to share a global variable among multiple workers, which causes that different workers use the same keystream to encrypt the plaintext.

## details

The bug is inside the worker_function. For example, in this scenario, the woker_0 is processing key_stream = aes.encrypt(pad(str(counter))), the counter is still 0, because the counter += 1 is not executed yet, what if the worker_1 is processing key_stream = aes.encrypt(pad(str(counter))) at the same time ? They are using the same keystream !

Find possible keys to decrypt flag:

Flag:
p4{at_the_end_of_the_day_you_can_only_count_on_yourself}

# Misc

Original Author: Terrynini