🧂 前言 今天我想要分享我在 ACSCCTF 2025 成功解出的其中一題 please recover my files ,這題是一個Linux Memory Forensic 的題目,剛好用到了前一天分享的知識,趁這次鐵人賽的機會寫成 Writeup 跟大家分享解題思路
🍘 仙貝工具 Volatility 下載連結:
Volatility 2 : https://github.com/volatilityfoundation/volatility
Volatility 3 : https://github.com/volatilityfoundation/volatility3
經典的記憶體分析工具,利用指令可以分析 memory dump 中的關鍵訊息,像是進程、網路連線、檔案……等等。
基本的操作這邊推薦可以直接上網查有人整理好的指令doc
而Volatility分為2與3版,差別就是一個是要使用 python2 執行,另一個是用 python3 執行,並且現在Volatility 2 已經沒有在維護了,但是一些 Plugin 的完整度在目前為止還是比 Volatility 3 還要高並且豐富,所以有些人還是會比較會偏向使用 Volatility 2 ,今天分享會主要使用 Volatility 3
Ida 下載連結:https://hex-rays.com/ida-pro
經典反編譯工具, 可以將執行檔反編譯成人可以看懂的程式碼
Cyberchef 網站連結:https://gchq.github.io/CyberChef/
非常好用的解密網站,支援多個加解密功能
題目大綱 這題的題意大致是,他發現了他的檔案被加密了,請恢復他,因此主要的目標是會需要找到怪怪的加密程式以及被加密的檔案,並分析他的加密程式是怎麼加密的並還原他。
調查開始 起手式先看一下他的作業系統以及他的版本是多少
得知說這個 memory dump 的作業系統為 Debian,而他 Linux 核心版本是 6.1.0-27,並且他是2024-11-01出現的,試試看pslist有沒有辦法出現東西,但因為官方給的Sysbol Table太少,因此高機率會出現 Symbol Table 缺失錯誤
因此可以試試看前一天提到的 https://github.com/Abyss-W4tcher/volatility3-symbols 有人已經幫忙整理的 Symbol Table 找找看,這邊剛好他有。
因此把對應版本的 Symbol Table 下載下來後放在V olatility 3 資料夾中的 volatility3\symbols\linux 路徑,再次用 pslist 看一下會不會成功。發現確實可以正常分析了
接下來下pstree 看一下有什麼值得看並且可疑的進程
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 PS C:\Users\yunshiuan\Desktop\Tools\volatility3 > python3 .\vol.py -f "C:\Users\yunshiuan\Desktop\asac\chall.raw" linux.pstree Volatility 3 Framework 2.26.2 Progress: 100.00 Stacking attempts finished OFFSET (V) PID TID PPID COMM 0x8a6100241fc0 1 1 0 systemd * 0x8a611367bf80 356 356 1 systemd-journal * 0x8a61015d5f40 388 388 1 systemd-udevd * 0x8a610b808000 440 440 1 systemd-timesyn * 0x8a6107958000 630 630 1 accounts-daemon * 0x8a6107959fc0 631 631 1 anacron * 0x8a610795bf80 632 632 1 avahi-daemon ** 0x8a610b823f80 646 646 632 avahi-daemon * 0x8a61015d0000 633 633 1 cron * 0x8a61159b5f40 634 634 1 dbus-daemon * 0x8a61159b1fc0 636 636 1 low-memory-moni * 0x8a61159b3f80 637 637 1 polkitd * 0x8a610794df40 638 638 1 power-profiles- * 0x8a6107948000 639 639 1 switcheroo-cont * 0x8a6107949fc0 640 640 1 systemd-logind * 0x8a610794bf80 641 641 1 udisksd * 0x8a6107965f40 652 652 1 NetworkManager * 0x8a6106740000 657 657 1 wpa_supplicant * 0x8a6112083f80 674 674 1 ModemManager * 0x8a61120a8000 693 693 1 cupsd * 0x8a61120a9fc0 707 707 1 gdm3 ** 0x8a6110c3bf80 1193 1193 707 gdm-session-wor *** 0x8a610b80df40 1273 1273 1193 gdm-wayland-ses **** 0x8a6108263f80 1283 1283 1273 gnome-session-b * 0x8a6103590000 724 724 1 cups-browsed * 0x8a61035a3f80 773 773 1 rtkit-daemon * 0x8a6108a4df40 893 893 1 upowerd * 0x8a610abfbf80 923 923 1 geoclue * 0x8a610abf8000 930 930 1 packagekitd * 0x8a6112e08000 938 938 1 colord * 0x8a610a445f40 1205 1205 1 systemd ** 0x8a6108a4bf80 1208 1208 1205 (sd-pam) ** 0x8a61120a0000 1223 1223 1205 pipewire ** 0x8a6107658000 1228 1228 1205 wireplumber ** 0x8a6107453f80 1232 1232 1205 pipewire-pulse ** 0x8a610a438000 1234 1234 1205 dbus-daemon ** 0x8a610a43df40 1235 1235 1205 gnome-keyring-d ** 0x8a610a439fc0 1244 1244 1205 gvfsd *** 0x8a6112e09fc0 2041 2041 1244 gvfsd-trash ** 0x8a6109c2df40 1258 1258 1205 gvfsd-fuse ** 0x8a6104c80000 1324 1324 1205 tracker-miner-f ** 0x8a6104c7df40 1332 1332 1205 gcr-ssh-agent ** 0x8a6104c78000 1333 1333 1205 gnome-session-c ** 0x8a6104c79fc0 1334 1334 1205 ssh-agent ** 0x8a6108265f40 1342 1342 1205 gnome-session-b *** 0x8a61027c1fc0 1360 1360 1342 at-spi-bus-laun **** 0x8a61027d1fc0 1373 1373 1360 dbus-daemon *** 0x8a6108105f40 1540 1540 1342 evolution-alarm *** 0x8a6112c89fc0 1569 1569 1342 gsd-disk-utilit *** 0x8a6109a73f80 1574 1574 1342 gnome-software ** 0x8a61027c5f40 1361 1361 1205 gvfs-udisks2-vo ** 0x8a61027d0000 1365 1365 1205 gnome-shell ** 0x8a61027d3f80 1376 1376 1205 gvfs-gphoto2-vo ** 0x8a61027c0000 1385 1385 1205 gvfs-goa-volume ** 0x8a6107d43f80 1395 1395 1205 goa-daemon ** 0x8a6107b70000 1408 1408 1205 goa-identity-se ** 0x8a6107b75f40 1417 1417 1205 gvfs-mtp-volume ** 0x8a6107b10000 1422 1422 1205 gvfs-afc-volume ** 0x8a6107ba1fc0 1460 1460 1205 xdg-permission- ** 0x8a6107ba3f80 1463 1463 1205 gnome-shell-cal ** 0x8a610815df40 1475 1475 1205 evolution-sourc ** 0x8a6108161fc0 1486 1486 1205 gjs ** 0x8a61080e9fc0 1488 1488 1205 at-spi2-registr ** 0x8a6108163f80 1498 1498 1205 sh *** 0x8a6112c8bf80 1568 1568 1498 ibus-daemon **** 0x8a615b9e1fc0 1689 1689 1568 ibus-dconf **** 0x8a615b9e0000 1690 1690 1568 ibus-extension- **** 0x8a61111a0000 1759 1759 1568 ibus-engine-sim ** 0x8a6108171fc0 1501 1501 1205 gsd-a11y-settin ** 0x8a6108175f40 1502 1502 1205 gsd-color ** 0x8a6108173f80 1503 1503 1205 gsd-datetime ** 0x8a6108170000 1505 1505 1205 gsd-housekeepin ** 0x8a6108180000 1508 1508 1205 gsd-keyboard ** 0x8a6108181fc0 1512 1512 1205 gsd-media-keys ** 0x8a6108185f40 1518 1518 1205 gsd-power ** 0x8a6108183f80 1519 1519 1205 gsd-print-notif ** 0x8a61080f1fc0 1526 1526 1205 gsd-rfkill ** 0x8a61080f3f80 1527 1527 1205 gsd-screensaver ** 0x8a611118df40 1536 1536 1205 gsd-sharing ** 0x8a611118bf80 1537 1537 1205 gsd-smartcard ** 0x8a6111188000 1538 1538 1205 gsd-sound ** 0x8a6111189fc0 1539 1539 1205 gsd-usb-protect ** 0x8a61111a1fc0 1541 1541 1205 gsd-wacom ** 0x8a6112c95f40 1570 1570 1205 evolution-calen ** 0x8a615b86df40 1633 1633 1205 gjs ** 0x8a615b9cbf80 1684 1684 1205 evolution-addre ** 0x8a615b9cdf40 1700 1700 1205 ibus-portal ** 0x8a615b9f5f40 1712 1712 1205 gsd-printer ** 0x8a6112088000 1745 1745 1205 xdg-desktop-por ** 0x8a6109a90000 1758 1758 1205 xdg-document-po *** 0x8a61661a5f40 1778 1778 1758 fusermount3 ** 0x8a615bbf8000 1789 1789 1205 xdg-desktop-por ** 0x8a610745df40 1830 1830 1205 xdg-desktop-por ** 0x8a6108859fc0 2015 2015 1205 gnome-calendar ** 0x8a61035a1fc0 2017 2017 1205 gnome-control-c ** 0x8a61035a0000 2021 2021 1205 gnome-terminal- *** 0x8a610b801fc0 2255 2255 2021 bash **** 0x8a6107ba0000 2258 2258 2255 su ***** 0x8a6112c9df40 2259 2259 2258 bash ****** 0x8a6110c3df40 2272 2272 2259 avml ** 0x8a6103431fc0 2102 2102 1205 dconf-service ** 0x8a610b813f80 2261 2261 1205 su ** 0x8a6108868000 2279 2279 1205 gvfsd-metadata * 0x8a6108f9bf80 1841 1841 1 fwupd
其中有趣的是在 102 行 他開了bash 後執行了 su ,之後底下又開了 bash 之後是 avml ,如果是bash的話有一個很不錯用的linux.bash Plugin 可以使用,它可以去看之前使用者在bash中輸入了什麼指令。
1 python3 .\vol.py - f "C:\U sers\y unshiuan\D esktop\a sac\c hall.raw" linux.bash
看到超可疑的 su 了,因為一般下 su 不會使用./ 在現在位置執行,因此可重點去看這個 su 在幹嘛。
首先先看這個 su 到底在哪裡
1 python3 .\vol.py -f "C:\Users\yunshiuan\Desktop\asac\chall.raw" linux.pagecache.Files > files.txt
ok 找到了 發現他被放在/home/acsc/su ,接下來知道他在哪之後就可以把它 Dump 出來
1 python3 .\vol.py -f "C:\Users\yunshiuan\Desktop\asac\chall.raw" linux.pagecache .InodePages --find '/home/acsc/su' --dump
成功把 su dump 出來了之後就可以直接開 ida 分析
開 ida 然後點一點就可以發現到一個酷酷的加密函式
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 unsigned __int64 sub_403175 () { unsigned __int64 result; char v1; char v2; char v3; int i; signed int v5; unsigned int v6; signed int v7; __int64 v8; unsigned __int64 j; unsigned __int64 k; __int64 v11; __int64 v12; __int64 v13; __int64 flag_content_len; __int64 flag_content; __int64 v16; __int64 v17; __int64 v18; char v19[48 ]; __int64 v20; char v21[48 ]; unsigned __int64 v22; char v23[16 ]; char flag_filename[16 ]; char filename[32 ]; char v26[32 ]; char v27[32 ]; char v28[40 ]; unsigned __int64 v29; v29 = __readfsqword(0x28u ); if ( (unsigned int )random_string(v23, 16LL ) == 1 ) { for ( i = 0 ; i <= 15 ; ++i ) { v28[2 * i] = *(_BYTE *)(qword_8E6110 + (((unsigned __int8)v23[i] >> 4 ) & 0xF )); v28[2 * i + 1 ] = *(_BYTE *)(qword_8E6110 + (v23[i] & 0xF )); } v28[32 ] = 0 ; printf ((__int64)&unk_8EA6C0, 40LL , (__int64)"AES_IV=%s" , v28); sub_6EF840(&unk_8EA6C0); if ( (unsigned int )random_string(v26, 32LL ) == 1 ) { sub_403086(v26, v27, 32LL ); xorenc(filename, &debian_log_addr, 19LL ); v11 = sub_6FC8B0(filename, "wb" ); if ( v11 ) { fwrite(v27, 1LL , 32LL , v11); fclose(v11); v5 = open(filename, 0 , v1); if ( v5 >= 0 ) { if ( !(unsigned int )sub_72C2D0((unsigned int )v5, v21) && (__int64)v22 > 0 ) { v12 = mmap(0LL ); if ( v12 != -1 ) { v13 = v12; for ( j = 0LL ; j < v22; j += 4096LL ) ; } } close(v5); } xorenc(flag_filename, &unk_7C1068, 15LL ); v6 = open(flag_filename, 2 , v2); if ( (v6 & 0x80000000 ) == 0 ) { if ( (unsigned int )sub_72C2D0(v6, v19) || (flag_content_len = v20, (flag_content = malloc (v20)) == 0 ) ) { close(v6); goto LABEL_34; } if ( read(v6, flag_content, flag_content_len) != flag_content_len ) { free (flag_content); close(v6); goto LABEL_34; } v16 = sub_402DEE(flag_content, flag_content_len, v26, v23, &v8); free (flag_content); if ( v16 ) { if ( (unsigned int )sub_72CBA0(v6, 0LL ) || sub_72C300(v6, 0LL , 0LL ) < 0 ) { free (v16); close(v6); goto LABEL_34; } write(v6, v16, v8); free (v16); v7 = open(flag_filename, 0 , v3); if ( v7 >= 0 ) { if ( !(unsigned int )sub_72C2D0((unsigned int )v7, v21) && (__int64)v22 > 0 ) { v17 = mmap(0LL ); if ( v17 != -1 ) { v18 = v17; for ( k = 0LL ; k < v22; k += 4096LL ) ; } } close(v7); } } close(v6); } } } } LABEL_34: result = v29 - __readfsqword(0x28u ); if ( result ) sub_72F780(); return result; }
分析一下他的執行邏輯
首先他先取了一個16 bytes 的隨機字串,並且經過了1 2 3 4 5 for ( i = 0 ; i <= 15 ; ++i ){ v28[2 * i] = *(_BYTE *)(qword_8E6110 + (((unsigned __int8)v23[i] >> 4 ) & 0xF )); v28[2 * i + 1 ] = *(_BYTE *)(qword_8E6110 + (v23[i] & 0xF )); }
這樣的加密過後輸出AES_IV=XXXX
再取一個 32 bytes 的隨機字串,對這個字串做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 27 28 29 30 31 32 33 34 35 36 37 char aAcsc2025[] = "acsc2025" unsigned __int64 __fastcall sub_403086(__int64 a1, __int64 a2, unsigned __int64 a3){ unsigned __int64 result; char v5; unsigned __int64 i; for ( i = 0LL ; ; ++i ) { result = i; if ( i >= a3 ) break ; v5 = *(_BYTE *)(a1 + i) ^ aAcsc2025[i % 8 ]; if ( ((7 * (_BYTE)i + 13 ) & 1 ) == 0 ) sub_402D55(); *(_BYTE *)(a2 + i) = ((((unsigned __int8)i ^ 0x13 ) + v5) << (8 - ((i & 7 ) + 1 ))) | ((int )(unsigned __int8)((i ^ 0x13 ) + v5) >> ((i & 7 ) + 1 )); } return result; } __int64 sub_402D55 () { int v0; __int64 result; int v2; int i; v2 = 305419896 ; for ( i = 0 ; i <= 4 ; ++i ) { v0 = i + 8 * v2; result = v0 ^ (unsigned int )v2; v2 ^= v0; } return result; }
這樣的加密之後,執行xorenc 將硬編碼進去的字串與key= “acsc2025” XOR 解密後以這個解密後的字串當作路徑將加密後過的隨機32 bytes 寫入進去
最後再使用xorenc 解密另一個硬編碼的字串與key= “acsc2025” XOR 解密後,將這個解密過後的路徑檔案內容先讀出來並且進行AES 加密後再寫回去,而AES 的key 是之前取的隨機16 bytes 的字串,IV 則也是之前取的隨機32 bytes 的字串
那這樣就可以整理等等解密的思路
先把 會經過XOR 解密的路徑先解密出來
得知 IV 與 KEY
最後AES 解密
XOR 解密字串 這邊首先先將硬編碼的兩個字串XOR解密,這邊直接使用 Cyberchef 首先先處理第一個 xor 字串解密
ok 知道了他會將隨機32 bytes 字串放進/var/log/debian.log中
現在解第二個xor解密字串
ok,這邊也得知道他會去/home/acsc/flag中將自容讀出來並加密他再寫回去。
有了以上資訊之後接下來要拿到 IV 與 KEY
IV 加密過後的 IV 我們可以知道他會輸出到console 上,因此我們可以直接試試看strings 這個 linux 記憶體dump 他把它輸出的AES_IV=XXX抓下來
1 strings "C:\U sers\y unshiuan\D esktop\a sac\c hall.raw" | Select-String - Pattern 'AES_IV'
成功抓到後,接下來寫解密腳本,這邊請AI代寫,把原本的隨機16 bytes 字串還原
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 #include <stdio.h> #include <string.h> #include <stdint.h> #include <stdlib.h> char hex_chars[] = "0123456789ABCDEF" ;void decode_hex_string (const char * hex_string, unsigned char * output, int length) { for (int i = 0 ; i < length; i++) { char high_char = hex_string[2 * i]; char low_char = hex_string[2 * i + 1 ]; int high_val = 0 , low_val = 0 ; if (high_char >= '0' && high_char <= '9' ) high_val = high_char - '0' ; else if (high_char >= 'A' && high_char <= 'F' ) high_val = high_char - 'A' + 10 ; else if (high_char >= 'a' && high_char <= 'f' ) high_val = high_char - 'a' + 10 ; if (low_char >= '0' && low_char <= '9' ) low_val = low_char - '0' ; else if (low_char >= 'A' && low_char <= 'F' ) low_val = low_char - 'A' + 10 ; else if (low_char >= 'a' && low_char <= 'f' ) low_val = low_char - 'a' + 10 ; output[i] = (high_val << 4 ) | low_val; } } void encode_hex_string (const unsigned char * input, char * output, int length) { for (int i = 0 ; i < length; i++) { output[2 * i] = hex_chars[(input[i] >> 4 ) & 0xF ]; output[2 * i + 1 ] = hex_chars[input[i] & 0xF ]; } output[2 * length] = '\0' ; } int main () { const char * hex_string = "33F27DE365AB35FFC29CB4FEEB506B34" ; int hex_length = strlen (hex_string); int binary_length = hex_length / 2 ; printf ("=== Hex Decoder ===\n\n" ); printf ("Input hex string: %s\n" , hex_string); printf ("Hex string length: %d\n" , hex_length); printf ("Expected binary length: %d bytes\n\n" , binary_length); unsigned char * decoded_data = malloc (binary_length); if (!decoded_data) { printf ("Error: Memory allocation failed\n" ); return 1 ; } decode_hex_string(hex_string, decoded_data, binary_length); printf ("Decoded binary data (hex): " ); for (int i = 0 ; i < binary_length; i++) { printf ("%02X" , decoded_data[i]); } printf ("\n\n" ); printf ("Decoded binary data (hex with spaces): " ); for (int i = 0 ; i < binary_length; i++) { printf ("%02X " , decoded_data[i]); } printf ("\n\n" ); printf ("Decoded binary data (bytes): " ); for (int i = 0 ; i < binary_length; i++) { printf ("%d " , decoded_data[i]); } printf ("\n\n" ); char * reencoded = malloc (hex_length + 1 ); if (reencoded) { encode_hex_string(decoded_data, reencoded, binary_length); printf ("Re-encoded verification: %s\n" , reencoded); if (strcmp (hex_string, reencoded) == 0 ) { printf ("✓ SUCCESS: Re-encoding matches original!\n" ); } else { printf ("✗ FAILED: Re-encoding does not match original!\n" ); } free (reencoded); } free (decoded_data); return 0 ; }
輸出為
1 2 3 4 5 6 7 8 9 10 11 12 13 14 === Hex Decoder === Input hex string: 33F27DE365AB35FFC29CB4FEEB506B34 Hex string length: 32 Expected binary length: 16 bytes Decoded binary data (hex): 33F27DE365AB35FFC29CB4FEEB506B34 Decoded binary data (hex with spaces): 33 F2 7D E3 65 AB 35 FF C2 9C B4 FE EB 50 6B 34 Decoded binary data (bytes): 51 242 125 227 101 171 53 255 194 156 180 254 235 80 107 52 Re-encoded verification: 33F27DE365AB35FFC29CB4FEEB506B34 ✓ SUCCESS: Re-encoding matches original!
得知到IV = 33F27DE365AB35FFC29CB4FEEB506B34
KEY 那之前的資訊可以知道他會寫在/var/log/debian.log中,因此我們直接把他從記憶體dump 出來
1 python3 .\vol.py -f "C:\Users\yunshiuan\Desktop\asac\chall.raw" linux.pagecache .InodePages --find '/var/log/debian.log' --dump
成功dump 之後直後就可以寫解密還原key,這邊一樣AI代寫解密腳本
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 #include <stdio.h> #include <string.h> #include <stdint.h> #include <stdlib.h> char aAcsc2025[] = "acsc2025" ;int64_t sub_402D55 () { int v0; int64_t result; int v2; int i; v2 = 305419896 ; for (i = 0 ; i <= 4 ; ++i) { v0 = i + 8 * v2; result = v0 ^ (unsigned int )v2; v2 ^= v0; } return result; } uint64_t decrypt_data (int64_t encrypted_data, int64_t output_buffer, uint64_t length) { uint64_t result; char v5; uint64_t i; for (i = 0LL ; ; ++i) { result = i; if (i >= length) break ; unsigned char encrypted_byte = *(uint8_t *)(encrypted_data + i); int rotation_amount = (i & 7 ) + 1 ; unsigned char rotated_value = (encrypted_byte >> (8 - rotation_amount)) | (encrypted_byte << rotation_amount); v5 = rotated_value - (i ^ 0x13 ); if (((7 * (uint8_t )i + 13 ) & 1 ) == 0 ) sub_402D55(); *(uint8_t *)(output_buffer + i) = v5 ^ aAcsc2025[i % 8 ]; } return result; } int main (int argc, char *argv[]) { if (argc != 2 ) { printf ("Usage: %s <encrypted_file>\n" , argv[0 ]); return 1 ; } FILE *file = fopen(argv[1 ], "rb" ); if (!file) { printf ("Error: Cannot open file %s\n" , argv[1 ]); return 1 ; } fseek(file, 0 , SEEK_END); long file_size = ftell(file); fseek(file, 0 , SEEK_SET); printf ("File size: %ld bytes\n" , file_size); unsigned char *encrypted_data = malloc (file_size); if (!encrypted_data) { printf ("Error: Memory allocation failed\n" ); fclose(file); return 1 ; } size_t bytes_read = fread(encrypted_data, 1 , file_size, file); fclose(file); if (bytes_read != file_size) { printf ("Error: Could not read entire file\n" ); free (encrypted_data); return 1 ; } printf ("Encrypted data (hex): " ); for (int i = 0 ; i < file_size && i < 50 ; i++) { printf ("%02x " , encrypted_data[i]); } if (file_size > 50 ) printf ("..." ); printf ("\n" ); unsigned char *decrypted_data = malloc (file_size + 1 ); if (!decrypted_data) { printf ("Error: Memory allocation failed\n" ); free (encrypted_data); return 1 ; } decrypt_data((int64_t )encrypted_data, (int64_t )decrypted_data, file_size); decrypted_data[file_size] = '\0' ; printf ("\nDecrypted data:\n" ); printf ("As hex (continuous): " ); for (int i = 0 ; i < file_size; i++) { printf ("%02X" , decrypted_data[i]); } printf ("\n\n" ); printf ("As hex (with spaces): " ); for (int i = 0 ; i < file_size; i++) { printf ("%02X " , decrypted_data[i]); } printf ("\n\n" ); printf ("As text (if printable): " ); for (int i = 0 ; i < file_size; i++) { if (decrypted_data[i] >= 32 && decrypted_data[i] <= 126 ) { printf ("%c" , decrypted_data[i]); } else { printf ("\\x%02X" , decrypted_data[i]); } } printf ("\n" ); char output_filename[256 ]; snprintf (output_filename, sizeof (output_filename), "%s.decrypted" , argv[1 ]); FILE *output_file = fopen(output_filename, "wb" ); if (output_file) { fwrite(decrypted_data, 1 , file_size, output_file); fclose(output_file); printf ("\nDecrypted data saved to: %s\n" , output_filename); } free (encrypted_data); free (decrypted_data); return 0 ; }
輸出
1 2 3 4 5 6 7 8 9 10 11 File size: 32 bytesEncrypted data (hex): 5 a 47 47 78 19 cc 3 e a3 26 58 2 a 14 33 a0 40 d0 b6 c8 d0 e4 0 b a3 d1 ea f5 df 65 83 0 f 04 9 f 36 Decrypted data:As hex (continuous): C0685A143E2D38BA50244B4A753A31810B42F62D68D2D1D381165153E0C3F01FAs hex (with spaces): C0 68 5 A 14 3 E 2 D 38 BA 50 24 4 B 4 A 75 3 A 31 81 0 B 42 F6 2 D 68 D2 D1 D3 81 16 51 53 E0 C3 F0 1 F As text (if printable): \xC0hZ\x14>-8 \xBAP$KJu:1 \x81\x0BB\xF6-h\xD2\xD1\xD3\x81\x16QS\xE0\xC3\xF0\x1FDecrypted data saved to: inode_0x8a6161fc0a78.dmp.decrypted
得知到KEY = C0685A143E2D38BA50244B4A753A31810B42F62D68D2D1D381165153E0C3F01F
解密flag 知道了key跟IV之後,就可將flag檔案dump 出來並進行解密,這邊AES解密也是交給Cyberchef
1 C:\Users\yunshiuan\Desktop\Tools\volatility3 > python3 .\vol.py -f "C:\Users\yunshiuan\Desktop\asac\chall.raw" linux.pagecache .InodePages --find '/home/acsc/flag' --dump
flag跑出來代表成功解出這題了!
總結 我覺得這題是挺不錯的,包含了Linux Memory Forensic 以及逆向的能力,賽中解出這題的成就感挺大的,確實如果我之前沒有接觸過 Linux 記憶體分析的話可能就會先耗在處理錯誤的地方一段時間。