A&D of Network Security - Lab 7

A&D of Network Security - Lab 7

tags: Practicum of A&D of NS NTU

Background

What is foremost and How to use it?

RSA CTF

Exercise - RSA

Given

1
2
3
4
p =  9648423029010515676590551740010426534945737639235739800643989352039852507298491399561035009163427050370107570733633350911691280297777160200625281665378483
q =  11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732443992230647903887510065547947313543299303261986053486569407
e =  65537
c =  83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605647316646576486526217457006376842280869728581726746401583705899941768214138742259689334840735633553053887641847651173776251820293087212885670180367406807406765923638973161375817392737747832762751690104423869019034

Then I can use decrypt function to fetch plaintext. Flag(hex): 12058e43d9e0c22559c19774 :::spoiler source code

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

1
2
3
4
5
6
-----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
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     $ 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
     >>> int("00ae5bb4f266003259cf9a6f521c3c03410176cf16df53953476eae3b21ede6c3c7b03bdca20b31c0067ffa797e4e910597873eef113a60feccd95deb5b2bf10066be2224ace29d532dc0b5a74d2d006f1", 16)
     3107418240490043721350750035888567930037346022842727545720161948823206440518081504556346829671723286782437916272838033415471073108501919548529007337724822783525742386454014691736602477652346609
    
  4. Factoring Small n Just using the online tool and you’ll get p and q p=1634733645809253848443133883865090859841783670033092312181110852389333100104508151212118167511579 q=1900871281664822113126851573935413975471896789968515493666638539088027103802104498957191261465571

  5. Decrypt Cipher
     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
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     $ 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
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     $ 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
    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
     $ 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
    1
    2
    3
    4
    5
    6
    7
     $ 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
    1
    2
    3
    4
    5
    6
    7
     $ 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
    1
    2
    3
    4
    5
    6
    7
    8
     $ 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, 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
     ./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

     #!/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 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, 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:
     $ 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
     $ 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!’)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    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
     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
     ...
     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.
     ...
     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 and try to unzip it.
     ...
     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?

Exercise 4

PNG (Portable Network Graphics) Specification CONFidence CTF Teaser A PNG Tale - Write Up Misc 总结 —-隐写术之图片隐写(二)

Exercise 5

Plaid CTF 2014: doge_stege doge_stege write up - 第8回資料 二進制處理方式

Exercise 6

Multimedia - 정말 커다란 이미지가 있습니다. PIL Image.getdata() 教程