Archive CTF

[TUCTF 2017] Reverse – Mainframe (300 points)

Mainframe

Files: mainframehacker.exe decompile_c.txt decompile_pseudo.txt
 

OK… so we are messing with an elf64 binary which was C source code, which was created by cobc (gnu-cobol), which had as input some COBOL source code. RIGHT!

That was really tricky challenge as it wasn’t easy at all, to find variable addresses and function parameters and understand what the heck it was doing. Running the binary was like that :-

$ ./mainframehacker.exe
Mainframe Hacking Tool v2.1
Important Note: Never, ever leave this tool behind
Enter Login Name: user
Welcome back, user
Enter Secure Login Code: 1234
INVALID CREDENTIALS

It simply asks for login and password and if they are valid we will get the flag. Time for reversing…

The only important function that we have to decompile is the NON__MALICIOUS__SOFTWARE_ which does the validation for the input credentials.

The gnu-cobol (libcob) firstly does a module initialization, which loads all the variables before starting executing COBOL code line by line with cob_set_location.

Let’s see the interesting variables initialized (see also attached decompile_c.txt) :-

Initialize module test.cbl
	login[60]=null
	password[90]=null
	password2[33]=null
	password_reversed[33]=null
	flag_bytes[33]=[
			b_15_7199 = 13618;
			byte_560AF9A5F2A2 = 48;
			b_16_7200 = 13872;
			byte_560AF9A5F2B2 = 54;
			b_17_7201 = 13872;
			...
			]

	invalid_char=false
	cur_char=null

First thing that we noticed, was the actual flag size, which is 33 bytes. The second thing was that there is another mess with the decompilation but probably that’s not IDA’s fault. Gnu-cobol seems to work with integers in a very weird way, this is probably for better compatibility with COBOL types(?).
So b_15_7199 = 13618 = 0×3532 = little-endian ( chr(0×35) + chr(0×32) ) = 25 and then byte_560AF9A5F2A2 = 48 = chr(48) = 0 gives us the first byte for the flag_bytes array which is 250.

Rather, than working in this way to find out the encrypted flag_bytes we reviewed the test.cbl modules initiliaztion dynamically :-

flag_bytes in memory
000055734ECF92A0  32 35 30 00 00 00 00 00  00 00 00 00 00 00 00 00  250.............
000055734ECF92B0  30 36 36 00 00 00 00 00  00 00 00 00 00 00 00 00  066.............
000055734ECF92C0  30 36 36 00 00 00 00 00  00 00 00 00 00 00 00 00  066.............
000055734ECF92D0  31 35 32 00 00 00 00 00  00 00 00 00 00 00 00 00  152.............
000055734ECF92E0  30 39 36 00 00 00 00 00  00 00 00 00 00 00 00 00  096.............
000055734ECF92F0  31 33 32 00 00 00 00 00  00 00 00 00 00 00 00 00  132.............
000055734ECF9300  30 39 36 00 00 00 00 00  00 00 00 00 00 00 00 00  096.............
000055734ECF9310  31 33 34 00 00 00 00 00  00 00 00 00 00 00 00 00  134.............
000055734ECF9320  31 39 30 00 00 00 00 00  00 00 00 00 00 00 00 00  190.............
000055734ECF9330  30 39 34 00 00 00 00 00  00 00 00 00 00 00 00 00  094.............
000055734ECF9340  32 33 38 00 00 00 00 00  00 00 00 00 00 00 00 00  238.............
000055734ECF9350  31 39 30 00 00 00 00 00  00 00 00 00 00 00 00 00  190.............
000055734ECF9360  32 32 30 00 00 00 00 00  00 00 00 00 00 00 00 00  220.............
000055734ECF9370  30 39 36 00 00 00 00 00  00 00 00 00 00 00 00 00  096.............
000055734ECF9380  32 33 30 00 00 00 00 00  00 00 00 00 00 00 00 00  230.............
000055734ECF9390  31 39 36 00 00 00 00 00  00 00 00 00 00 00 00 00  196.............
000055734ECF93A0  30 39 38 00 00 00 00 00  00 00 00 00 00 00 00 00  098.............
000055734ECF93B0  31 34 32 00 00 00 00 00  00 00 00 00 00 00 00 00  142.............
000055734ECF93C0  31 39 30 00 00 00 00 00  00 00 00 00 00 00 00 00  190.............
000055734ECF93D0  31 30 32 00 00 00 00 00  00 00 00 00 00 00 00 00  102.............
000055734ECF93E0  32 30 38 00 00 00 00 00  00 00 00 00 00 00 00 00  208.............
000055734ECF93F0  31 36 38 00 00 00 00 00  00 00 00 00 00 00 00 00  168.............
000055734ECF9400  31 39 30 00 00 00 00 00  00 00 00 00 00 00 00 00  190.............
000055734ECF9410  32 31 34 00 00 00 00 00  00 00 00 00 00 00 00 00  214.............
000055734ECF9420  31 33 34 00 00 00 00 00  00 00 00 00 00 00 00 00  134.............
000055734ECF9430  31 30 34 00 00 00 00 00  00 00 00 00 00 00 00 00  104.............
000055734ECF9440  31 34 34 00 00 00 00 00  00 00 00 00 00 00 00 00  144.............
000055734ECF9450  32 34 36 00 00 00 00 00  00 00 00 00 00 00 00 00  246.............
000055734ECF9460  31 34 30 00 00 00 00 00  00 00 00 00 00 00 00 00  140.............
000055734ECF9470  31 36 38 00 00 00 00 00  00 00 00 00 00 00 00 00  168.............
000055734ECF9480  31 33 34 00 00 00 00 00  00 00 00 00 00 00 00 00  134.............
000055734ECF9490  31 37 30 00 00 00 00 00  00 00 00 00 00 00 00 00  170.............
000055734ECF94A0  31 36 38 00 00 00 00 00  00 00 00 00 00 00 00 00  168.............

We continue to decompiling careful with the same way the rest of the code and the following pseudo code was constructed :-

Initialize module test.cbl
	login[60]=null
	password[90]=null
	password2[33]=null
	password_reversed[33]=null
	flag_bytes[33]=[.....]
	invalid_char=false
	cur_char=null

MAIN-PROCEDURE:DISPLAY
	Mainframe Hacking Tool v2.1
	Important Note: Never, ever leave this tool behind
	Enter Login Name:

LOGIN-PROCUDURE:ACCEPT
	login

LOGIN-PROCUDURE:DISPLAY
	Welcome back, 
	Enter Secure Login Code:

LOGIN-PROCUDURE:ACCEPT
	password

LOGIN-PROCUDURE:MOVE
	password -> password2

LOGIN-PROCUDURE:MOVE
	reverse(password2)-> password_reverse

LOGIN-PROCUDURE:PERFORM
	for ( iter_i = 0; iter_i < 34; iter_i++ ) {
		STRMOD:COMPUTE
			cur_char=ord(password_reverse[i]) * 2

		STRMOD:IF
			if ( cur_char != flag_bytes[i] )
				STRMOD:MOVE
					invalid_char = true
	}
	if ( invalid_char == true )
		DISPLAY
			INVALID CREDENTIALS
	else
		DISPLAY
			Nice Try Hackeman!
			You got the flag but I have escaped!

As our pseudo code looks much more readable from the gnu-cobol one... we can finally see that it just reverses our input and multiplies each char by 2 and then compares it with the flag_bytes.
So we just need to divide the numbers that we found in memory by 2 and then reverse them to get the flag.

#!/usr/bin/python
flag_bytes=[250,66,66,152,96,132,96,134,190,94,238,190,220,96,230,196,98,142,190,102,208,168,190,214,134,104,144,246,140,168,134,170,168]
print "".join([chr(x/2) for x in flag_bytes])[::-1]

# TUCTF{H4Ck_Th3_G1bs0n_w/_C0B0L!!}

PS: That was the only flag that we didn't capture as we solved this just after the CTF was finished. But flags are still flags without points :)

Share
Photo

root

November 29th

CTF

[TUCTF 2017] Misc – Gr8 Pictures 2 (150 points)

Gr8 Pictures 2

Files: flag.png flag50As.png flag50Bs.png gr8pic2.py

 

Similarly to Gr8 Pictures 1 to find out how we can decrypt the hidden message from the flag.png, we will have to mess around with the service on gr8pics2.tuctf.com:5555. However, this one looks more tricky! The received pictures were different from the flag.png picture. So, for better understanding of the encryption service, we decided to receive two pictures for further analysis (one with 50A’s and one with 50B’s) :-

$ python -c "print 'A'*50" | nc gr8pics2.tuctf.com 5555 | base64 -d > flag50As.png
$ python -c "print 'B'*50" | nc gr8pics2.tuctf.com 5555 | base64 -d > flag50Bs.png

Again, by running a binary diff, but now for both of our pictures (not the flag.png) we came up with the following findings :-

We quickly notice, that the 1st CRC of IDAT png sections is the place for our hidden message. Firstly, we thought that we can create again the XOR key, by XORing the (A) 0×41 ^ 0×67 but that didn’t work. So the next try was to run pngcheck on the image as it was broken because of the CRC :-

$ pngcheck -v7f flag50As.png
File: flag50As.png (1774976 bytes)
  chunk IHDR at offset 0x0000c, length 13
    1920 x 1080 image, 24-bit RGB, non-interlaced
  chunk iCCP at offset 0x00025, length 2615
    profile name = sRGB IEC61966-2.1, compression method = 0 (deflate)
    compressed profile = 2596 bytes
  chunk pHYs at offset 0x00a68, length 9: 2834x2834 pixels/meter (72 dpi)
  chunk IDAT at offset 0x00a7d, length 8192
    zlib: deflated, 32K window, default compression
  CRC error in chunk IDAT (computed 26649350, expected 67649350)
  chunk IDAT at offset 0x02a89, length 8192
  CRC error in chunk IDAT (computed 0f95f454, expected 4e95f454)
  chunk IDAT at offset 0x04a95, length 8192
  CRC error in chunk IDAT (computed 9a20ab2c, expected db20ab2c)
  chunk IDAT at offset 0x06aa1, length 8192
  CRC error in chunk IDAT (computed 7b6b58e8, expected 3a6b58e8)
  chunk IDAT at offset 0x08aad, length 8192
  CRC error in chunk IDAT (computed 210564a1, expected 600564a1)
  chunk IDAT at offset 0x0aab9, length 8192
  CRC error in chunk IDAT (computed 24250c73, expected 65250c73)
  chunk IDAT at offset 0x0cac5, length 8192
  CRC error in chunk IDAT (computed 15085e3c, expected 54085e3c)
  ...

Let’s focus on the first CRC error “CRC error in chunk IDAT (computed 26649350, expected 67649350)”. The encryption service changed the 1st CRC byte to 0×67 when it should be 0×26. Let’s XOR those chr( 0×67 ^ 0×26 ) give us the letter ‘A’! So, we get the XOR key by correcting the CRC values. Let’s create the key and cipher with the help of pngcheck and then write some python code to XOR them :-

$ pngcheck -v7f flag.png | grep "\s*CRC" | sed 's/.*computed //g' | sed 's/,.*//g' | grep -o "^.." | tr -d "\n" > key
	# 9260d5f43aba7e95afb4c504da2eaf84155c33306c5720529b6316a6218b32b281d609123c31e23c73b36e62dcfc70e821fe

$ pngcheck -v7f flag.png | grep "\s*CRC" | sed 's/.*expected //g' | tr -d ")" | grep -o "^.." | tr -d "\n" > cipher
	# c63596a07cc14efb9ecd9a35e91d98a04a3f735e33251312ff3c7b9552f872d5e4a5567a555586591dec070c83bf22ab5283
#!/usr/bin/python

key=open('key','rb').read().decode('hex')
cipher=open('cipher','rb').read().decode('hex')

flag = ""
for i in range(len(cipher)):
	flag += chr(ord(cipher[i]) ^ ord(key[i]))

print flag

# TUCTF{0n1y_1337$_c@n_r3@d_m3ss@ges_hidden_in_CRCs}
Share
Photo

root

November 29th

CTF

[TUCTF 2017] Misc – Gr8 Pictures (50 points)

Gr8 Pictures


Files: flag.png flag50As.png gr8pic.py

 

To decrypt the hidden message from the flag.png picture we will need to find out how the service running on gr8pics.tuctf.com:4444 encrypts the provided text message within the picture. By messing around with the service we find out out that we need to provide exactly 50 bytes to receive back a base64 string which contains the picture with our message encrypted. So let’s try to provide 50 A’s for a message and see how it goes :-

$ python -c "print 'A'*50" | nc gr8pics.tuctf.com 4444 | base64 -d > flag50As.png

The flag50As.png received picture has the same size and looks the same as the flag.png. So let’s run a binary diff to these images :-

The results from the binary diff are clear enough. We can see that from the offset 0×335371 with step 8 bytes for 50 bytes we have all the differences. But how can we decrypt the hidden message? This requires some guessing… So we provided 50 A’s (0×41) and at the first position we got 0×08, what if the XOR operation of those give us the key to decrypt all the encrypted messages for any picture? By trying this chr( (0×41 ^ 0×08) ^ (0x1d) ) give us the letter ‘T’ -the first letter for the flag- so let’s write some python code to get the flag!

#!/usr/bin/python

f1=open('flag.png','rb').read()
f2=open('flag50As.png','rb').read()

flag=""
for i in range(0x335371,0x335371+(8*50),8):
	flag += chr(ord(f1[i:i+1]) ^ ord(f2[i:i+1]) ^ ord('A'))

print flag

# TUCTF{st3g@n0gr@phy's_so_c00l,No0ne_steals_my_msg}
Share
Photo

root

November 29th

CTF
line

© 2017 SuRGeoNix | Security Blog