---
# System prepended metadata

title: A&D of Network Security - Lab 7
tags: [NTU_PADNS, NTU]

---

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