# 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`

:::
### 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


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
:::

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.
:::

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/)