# A&D of Network Security - Lab 7 ###### tags: `Practicum of A&D of NS` `NTU` ## Background [What is foremost and How to use it?](https://darkranger.no-ip.org/archives/v5/document/linux/foremost_recovery.htm) ## RSA CTF ### Exercise - RSA Given ``` p = 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297777160200625281665378483 q = 11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732443992230647903887510065547947313543299303261986053486569407 e = 65537 c = 83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034 ``` Then I can use decrypt function to fetch plaintext. Flag(hex): `12058e43d9e0c22559c19774` :::spoiler source code ```python! from Crypto.Util.number import long_to_bytes, inverse, bytes_to_long p = 9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297777160200625281665378483 q = 11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732443992230647903887510065547947313543299303261986053486569407 e = 65537 c = 83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034 n = p * q phi = (q-1)*(p-1) d = inverse(e, phi) print(long_to_bytes(pow(c,d,n)).hex()) ``` ::: ### Exercise - Decrypt_RSA Given `public-key.pem` and `flag.txt` File: `public-key.pem` ``` -----BEGIN PUBLIC KEY----- MGwwDQYJKoZIhvcNAQEBBQADWwAwWAJRAK5btPJmADJZz5pvUhw8A0EBds8W31OV NHbq47Ie3mw8ewO9yiCzHABn/6eX5OkQWXhz7vETpg/szZXetbK/EAZr4iJKzinV MtwLWnTS0AbxAgMBAAE= -----END PUBLIC KEY----- ``` 1. Recon Obviously, you can notice that public key file is generated by such like `openssl` tool. So, there's a way to turn it to text. 2. Transform ```bash $ openssl rsa -pubin -in public-key.pem -text RSA Public-Key: (640 bit) Modulus: 00:ae:5b:b4:f2:66:00:32:59:cf:9a:6f:52:1c:3c: 03:41:01:76:cf:16:df:53:95:34:76:ea:e3:b2:1e: de:6c:3c:7b:03:bd:ca:20:b3:1c:00:67:ff:a7:97: e4:e9:10:59:78:73:ee:f1:13:a6:0f:ec:cd:95:de: b5:b2:bf:10:06:6b:e2:22:4a:ce:29:d5:32:dc:0b: 5a:74:d2:d0:06:f1 Exponent: 65537 (0x10001) writing RSA key -----BEGIN PUBLIC KEY----- MGwwDQYJKoZIhvcNAQEBBQADWwAwWAJRAK5btPJmADJZz5pvUhw8A0EBds8W31OV NHbq47Ie3mw8ewO9yiCzHABn/6eX5OkQWXhz7vETpg/szZXetbK/EAZr4iJKzinV MtwLWnTS0AbxAgMBAAE= -----END PUBLIC KEY----- ``` Then after the transformation, you can see that `n=p*q=0x00ae...` and `e=65537` 3. Hex to Decimal ```bash! >>> int("00ae5bb4f266003259cf9a6f521c3c03410176cf16df53953476eae3b21ede6c3c7b03bdca20b31c0067ffa797e4e910597873eef113a60feccd95deb5b2bf10066be2224ace29d532dc0b5a74d2d006f1", 16) 3107418240490043721350750035888567930037346022842727545720161948823206440518081504556346829671723286782437916272838033415471073108501919548529007337724822783525742386454014691736602477652346609 ``` 4. Factoring Small `n` Just using the [online tool](http://factordb.com/index.php) and you'll get `p` and `q` `p=1634733645809253848443133883865090859841783670033092312181110852389333100104508151212118167511579` `q=1900871281664822113126851573935413975471896789968515493666638539088027103802104498957191261465571` 5. Decrypt Cipher ```python= from Crypto.Util.number import long_to_bytes, inverse, bytes_to_long, isPrime from base64 import b64decode cipher = open("./flag.txt", "rb").read().hex() cipher = int(cipher, 16) p = 1634733645809253848443133883865090859841783670033092312181110852389333100104508151212118167511579 q = 1900871281664822113126851573935413975471896789968515493666638539088027103802104498957191261465571 e = 65537 n = p * q phi = (q-1)*(p-1) d = inverse(e, phi) print(bytes.fromhex(long_to_bytes(pow(cipher,d,n)).hex()).decode("cp437")) ``` Flag: `FLAG_IS_WeAK_rSA` ## Stego CTF ### Exercise 1 - `zip` Extension 1. Use `binwalk` to Recon ```bash $ binwalk Exercise\ 1\ -\ example.jpg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, EXIF standard 12 0xC TIFF image data, big-endian, offset of first image directory: 8 9298 0x2452 TIFF image data, little-endian offset of first image directory: 662 9692 0x25DC JPEG image data, JFIF standard 1.01 14241 0x37A1 Copyright string: "Copyright 2003 Apple Computer Inc., all rights reserved." 1972141 0x1E17AD Zip archive data, at least v1.0 to extract, compressed size: 20, uncompressed size: 20, name: secret.txt 1972309 0x1E1855 End of Zip archive, footer length: 22 ``` You can see that it contained a `zip` file at the end of data 2. Change Extension Thus, you can change the extension manually and `unzip` it 3. Get Secret :::spoiler secret flag `supa_secret_flagzor` ::: ### Exercise 2 - Foremost 1. Recon ```python $ pngcheck Exercise\ 2\ -\ PurpleThing.png Exercise 2 - PurpleThing.png EOF while reading CRC value ERROR: Exercise 2 - PurpleThing.png $ binwalk Exercise\ 2\ -\ PurpleThing.png DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 PNG image, 3200 x 2953, 8-bit/color RGBA, non-interlaced 85 0x55 Zlib compressed data, best compression 2757 0xAC5 Zlib compressed data, best compression 765455 0xBAE0F JPEG image data, JFIF standard 1.01 765485 0xBAE2D TIFF image data, big-endian, offset of first image directory: 8 1809691 0x1B9D1B StuffIt Deluxe Segment (data): f ``` First, I used `pngcheck` to observe the data structure and it returned CRC error means something wrong at the end of file. Therefore, I used `binwalk` to check the extra information. It seemed has another file in it. 2. Use `Foremost` to parse hiding files ```bash $ foremost -v Exercise\ 2\ -\ PurpleThing.png Foremost version 1.5.7 by Jesse Kornblum, Kris Kendall, and Nick Mikus Audit File Foremost started at Mon Apr 10 22:47:57 2023 Invocation: foremost -v Exercise 2 - PurpleThing.png Output directory: /mnt/d/NTU/First Year/2nd semester/Practicum of Attacking and Defense of Network Security/Lab/Lec07/Practice/output Configuration file: /etc/foremost.conf Processing: Exercise 2 - PurpleThing.png |------------------------------------------------------------------ File: Exercise 2 - PurpleThing.png Start: Mon Apr 10 22:47:57 2023 Length: 2 MB (2354256 bytes) Num Name (bs=512) Size File Offset Comment 0: 00001495.jpg 1 MB 765455 *| Finish: Mon Apr 10 22:47:57 2023 1 FILES EXTRACTED jpg:= 1 ------------------------------------------------------------------ Foremost finished at Mon Apr 10 22:47:57 2023 ``` Then it'll extract a folder named `output` :::spoiler `secret flag` ![](https://i.imgur.com/EQGV3sN.jpg) ::: ### Exercise 3 - Unzip twice 1. Use `binwalk` to recon ```bash $ binwalk Exercise\ 3\ -\ stego2.jpg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.01 40804 0x9F64 Zip archive data, at least v2.0 to extract, compressed size: 32993, uncompressed size: 33783, name: got2.jpg 73941 0x120D5 End of Zip archive, footer length: 22 ``` 2. Unzip it 3. `binwalk` again ```bash $ binwalk got2.jpg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.02 33587 0x8333 Zip archive data, at least v1.0 to extract, compressed size: 32, uncompressed size: 32, name: txt.txt 33761 0x83E1 End of Zip archive, footer length: 22 ``` 4. Unzip it again 5. Get flag :::spoiler Flag: `6307834008eb8edbe18c7a20ee4a909d` ::: ### Exercise 4 1. Recon ```bash $ pngcheck -cv Exercise\ 4\ -\ stego1.png File: Exercise 4 - stego1.png (11037 bytes) chunk IHDR at offset 0x0000c, length 13 800 x 800 image, 24-bit RGB, non-interlaced chunk IDAT at offset 0x00025, length 10980 zlib: deflated, 32K window, default compression chunk IEND at offset 0x02b15, length 0 No errors detected in Exercise 4 - stego1.png (3 chunks, 99.4% compression). ``` Seems there is nothing special information however, TA's hint is `png filter`. Refer to [W3C spec](https://www.w3.org/TR/PNG-Filters.html), I am aware that there are several filter types that can be applied, such as None `(byte 0x0)`, Sub `(byte 0x1)`, Up `(byte 0x2)` etc. Bytes of these filter types can be used to hide information, i.e. the flag, in the PNG. In our case, we have to decompress the PNG `IDAT` chunk data and extract the filter type bytes from the decompressed data. 2. Decompressed Flag by Using Script below ```bash! ./Exercise\ 4-exp.py Exercise\ 4\ -\ stego1.png PNG Signature: (b'\x89', b'P', b'N', b'G', b'\r', b'\n', b'\x1a', b'\n') Pos : 8 Type: b'IHDR' Size: 13 CRC : b'5412913F' Pos : 33 Type: b'IDAT' Size: 10980 CRC : b'98F96EEB' Pos : 11025 Type: b'IEND' Size: 0 CRC : b'AE426082' Data length in PNG file : 10980 Decompressed data length: 1920800 Flag: DrgnS{WhenYouGazeIntoThePNGThePNGAlsoGazezIntoYou} ``` :::spoiler source code ```python= #!/usr/bin/env python from struct import unpack from binascii import hexlify, unhexlify import sys, zlib # Returns [Position, Chunk Size, Chunk Type, Chunk Data, Chunk CRC] def getChunk(buf, pos): a = [] a.append(pos) size = unpack('!I', buf[pos:pos+4])[0] # Chunk Size a.append(buf[pos:pos+4]) # Chunk Type a.append(buf[pos+4:pos+8]) # Chunk Data a.append(buf[pos+8:pos+8+size]) # Chunk CRC a.append(buf[pos+8+size:pos+12+size]) return a def printChunk(buf, pos): print('Pos : '+str(pos)+'') print('Type: ' + str(buf[pos+4:pos+8])) size = unpack('!I', buf[pos:pos+4])[0] print('Size: ' + str(size)) #print('Cont: ' + str(hexlify(buf[pos+8:pos+8+size]))) print('CRC : ' + str(hexlify(buf[pos+size+8:pos+size+12]).upper())) print if len(sys.argv)!=2: print('Usage: ./this Stegano_PNG') sys.exit(2) with open(sys.argv[1], 'rb') as f: buf = f.read() pos=0 print("PNG Signature: " + str(unpack('cccccccc', buf[pos:pos+8]))) pos+=8 chunks = [] for i in range(3): chunks.append(getChunk(buf, pos)) printChunk(buf, pos) pos+=unpack('!I',chunks[i][1])[0]+12 decompressed = zlib.decompress(chunks[1][3]) # Decompressed data length = height x (width * 3 + 1) print("Data length in PNG file : ", len(chunks[1][3])) print("Decompressed data length: ", len(decompressed)) height = unpack('!I',(chunks[0][3][4:8]))[0] width = unpack('!I',(chunks[0][3][:4]))[0] blocksize = width * 3 + 1 filterbits = '' for i in range(0,len(decompressed),blocksize): bit = unpack('2401c', decompressed[i:i+blocksize])[0] if bit == b'\x00': filterbits+='0' elif bit == b'\x01': filterbits+='1' else: print('Bit is not 0 or 1... Default is 0 - MAGIC!') sys.exit(3) s = filterbits endianess_filterbits = [filterbits[i:i+8][::-1] for i in range(0, len(filterbits), 8)] flag = '' for x in endianess_filterbits: if x=='00000000': break flag += unhexlify('%x' % int('0b'+str(x), 2)).decode() print('Flag: ' + flag) ``` ::: ### Exercise 5 - Change Palette 1. Recon First, I used [online tool](https://aperisolve.fr/) to fetch some info but all of them are useless. So, I used `stegsolve.jar` to analyze the figure, and something blurry appeared at the right of picture shown below ![](https://i.imgur.com/p3rSNzy.png) ![](https://i.imgur.com/jtwsvlp.png) 2. Try to change palette I found a useful code on [Stack Overflow](https://stackoverflow.com/questions/1158736/changing-palettes-of-8-bit-png-images-using-python-pil/1214765#1214765), and after some debugging, I can use it successfully. This program is aimed to allow us to write out 256 images, each one highlighting a single entry in white while blacking out the others: ```bash! $ for i in {0..255}; do ./change_palette.py doge_stege.png "single-color-${i}.png" "${i}"; done ``` :::info At the first time using, you should uncomment line 32 and comment line 34 to find which page can be decrypted ::: ![](https://i.imgur.com/foDYV20.png) You can observe that `single-color-127.png` has some strings that we met in `stegsolve.jar`, so that is the magic number that we can continue decrypting. 3. Continue changing palette ```bash! $ for i in {0..128}; do ./change_palette.py doge_stege.png "range-color-127+${i}.png" "${i}"; done ``` :::info Note that you should uncomment line 34 and comment line 32 to decrypt it further. ::: ![](https://i.imgur.com/Qu62Q6l.png) You can notice that most of the results have clearly flag strings in the pictures. :::spoiler source code ```python= #!/usr/bin/env python import sys import struct from zlib import crc32 import os # PNG file format signature pngsig = b'\x89PNG\r\n\x1a\n' def swap_palette(filename, n): # open in read+write mode with open(filename, 'r+b') as f: f.seek(0) # verify that we have a PNG file if f.read(len(pngsig)) != pngsig: raise RuntimeError('not a png file!') while True: chunkstr = f.read(8) if len(chunkstr) != 8: # end of file break # decode the chunk header length, chtype = struct.unpack('>L4s', chunkstr) # we only care about palette chunks if chtype == b'PLTE': curpos = f.tell() paldata = f.read(length) # replace palette entry n with white, the rest with black # paldata = ("\x00\x00\x00" * n) + "\xff\xff\xff" + ("\x00\x00\x00" * (256 - n - 1)) # replace palette entry 127 to 127 + n with white, the rest with black paldata = (b"\x00\x00\x00" * 127) + (b"\xff\xff\xff"*n) + (b"\x00\x00\x00" * (256 - (127 + n))) # go back and write the modified palette in-place f.seek(curpos) f.write(paldata) f.write(struct.pack('>L', crc32(chtype+paldata)&0xffffffff)) else: # skip over non-palette chunks f.seek(length+4, os.SEEK_CUR) if __name__ == '__main__': import shutil shutil.copyfile(sys.argv[1], sys.argv[2]) swap_palette(sys.argv[2], int(sys.argv[3])) ``` ::: ### Exercise 6 TA's Hint: LSB and brute force 1. Fetch LSB from Each Pixel ```python! from PIL import Image FLAG = "" flag_image = Image.open("./Exercise 6 - bonas.png") pixel = flag_image.getdata() for i in pixel: FLAG += bin(i)[-1] ``` 2. Observe the data type After getting LSB from each pixel, you can observe the extension of this file from the beginning bytes ```python! ... for i in range(4): print(chr(int(FLAG[i*8:(i+1)*8], 2))) ... ``` The output is: `Rar!` 3. Transform a proper file So, we can modify the file extension and store it in bytes. ```python! ... output = open("./data.rar", "wb") for j in range(0, len(FLAG), 8): output.write(chr(int(FLAG[j:j+8], 2)).encode()) ... ``` 4. Brute force to decrypt it However if we'd like to unzip it, it needs 5 char password. So, I found a wordlist in [this page](https://www.bestwordlist.com/5letterwords.htm) and try to unzip it. ```python! ... import subprocess, re password = open("password.txt", "r").read().split(" ") for passwd in password: # print(passwd) result = subprocess.getstatusoutput("unrar x data.rar - inul -p" + passwd) if result[0] == 0: print("Sucess!! Password is {}".format(passwd)) break ... ``` 5. After Unzip it You'll get a `flag.txt` and the flag is: `LSB_is_ubiquitous` ## Reference ### Exercise Decrypt_RSA [How do I use the openssl command to decode a public key .PEM file?](https://superuser.com/questions/1644533/how-do-i-use-the-openssl-command-to-decode-a-public-key-pem-file) ### Exercise 4 [PNG (Portable Network Graphics) Specification](https://www.w3.org/TR/PNG-Filters.html) [CONFidence CTF Teaser A PNG Tale - Write Up](https://github.com/ctfs/write-ups-2015/blob/master/confidence-ctf-teaser-2015/stegano/a-png-tale-200/README.md) [Misc 总结 ----隐写术之图片隐写(二)](https://xz.aliyun.com/t/1836) ### Exercise 5 [Plaid CTF 2014: doge_stege](https://github.com/ctfs/write-ups-2014/blob/master/plaid-ctf-2014/doge-stege/README.md) [doge_stege write up - 第8回資料](https://wiki.jyoken.net/ctf/lesson/%E7%AC%AC8%E5%9B%9E) [二進制處理方式](https://kuanyuchen.gitbooks.io/python3-tutorial/content/er_jin_zhi_chu_li_fang_shi.html) ### Exercise 6 [Multimedia - 정말 커다란 이미지가 있습니다.](https://whitesnake1004.tistory.com/352) [PIL Image.getdata() 教程](https://liam.page/2017/08/06/pil-tutorial-pixel-operations-and-image-filter/)