Flare-On 11 Notes (2024) === # Official Posts - [Announcement](https://cloud.google.com/blog/topics/threat-intelligence/announcing-eleventh-annual-flare-on-challenge/) - [ctfd](https://flare-on11.ctfd.io/challenges) - [my workspace](https://1drv.ms/f/s!Aj6sTe2Xl3tDgfkLLOI3JyWS4EuUpg?e=Lj0Mb3) - [solutions](https://cloud.google.com/blog/topics/threat-intelligence/flareon-11-challenge-solutions) # 心得 - 6/10 - 595 th (8 th in Taiwan) ![image](https://hackmd.io/_uploads/BJw2Z0nW1g.png) - 每次在做 flare-on 的題目都會學到很多東西 - 感謝 chatgpt 一路陪伴學習 - 學習到 coredump 分析、Verilog 等平時不會處碰到的領域 - 練習到對於非 C++ 程式分析方法 (Golang, dotnet AOT), FLIRT Signature 的使用 - 自己在面對未知或新的東西還是有一些阻力, 當下會很抗拒 -> 導致最後沒有解出 ECC 的問題 - 好想破台 # Finished ## Flags > challenge 1: `welcome_to_11@flare-on.com` > challenge 2: `Th3_M4tH_Do_b3_mAth1ng@flare-on.com` > challenge 3: `1RuleADayK33p$Malw4r3Aw4y@flare-on.com` > challenge 4: `wh0a_it5_4_cru3l_j4va5cr1p7@flare-on.com` > challenge 5: `supp1y_cha1n_sund4y@flare-on.com` > challenge 6: `please_send_help_i_am_trapped_in_a_ctf_flag_factory@flare-on.com` ## Challenge 1 - frog: python source code review ~~pyinstaller~~ > filename: `frog.7z` > filetype: `python source` (and some other files) > estimated time: 1 min - 題目敘述 ``` Welcome to Flare-On 11! Download this 7zip package, unzip it with the password 'flare', and read the README.txt file for launching instructions. It is written in PyGame so it may be runnable under many architectures, but also includes a pyinstaller created EXE file for easy execution on Windows. Your mission is get the frog to the "11" statue, and the game will display the flag. Enter the flag on this page to advance to the next stage. All flags in this event are formatted as email addresses ending with the @flare-on.com domain. ``` - 解題思路 這題一看是 Python 寫的, 並且有附原始碼, 直接可以找到 decode flag 的程式碼 - solve.py ```python def decode_flag(key): encoded = "\xa5\xb7\xbe\xb1\xbd\xbf\xb7\x8d\xa6\xbd\x8d\xe3\xe3\x92\xb4\xbe\xb3\xa0\xb7\xff\xbd\xbc\xfc\xb1\xbd\xbf" return ''.join([chr(ord(c) ^ key) for c in encoded]) for i in range(256): if "@flare-on.com" in decode_flag(i): print(decode_flag(i)) ``` ## Challenge 2 - checksum: golang > filename: `checksum.exe` > filetype: `PE64` > estimated time: 3 hours ~ 24 hours - 題目敘述 ``` We recently came across a silly executable that appears benign. It just asks us to do some math... From the strings found in the sample, we suspect there are more to the sample than what we are seeing. Please investigate and let us know what you find! 7zip archive password: flare ``` - 解題經歷 這是一支 Golang 寫的程式, 因為是不熟的程式語言, 所以我的做法是邊寫 Golang 程式(chatgpt)邊逆寫好的程式, 再去比對題目裡的 pesudo code, 在把 Golang 程式丟進 IDA Pro 之後, 人寫的 code 基本上會排在一起, 且開頭會是 `main_*`, 所以這題只要專注分析 `main_main`, `main_a` 及 `main_b` - 逆向工程 - main_main - 計算三個加法問題 `Check sum: %d + %d = ` - 輸入一個值 (checksum) 長度應為 64 的 hexstring `Checksum: `, 後來發現這個值會用來當 chacha20 的加解密參數 - nonce: first 24 bytes of hex decoded input - key: hex decoded input - decrypt data xchacha20poly1305 - main_a - xor checksum input with `FlareOn2024` - `cQoFRQErX1YAVw1zVQdFUSxfAQNRBXUNAxBSe15QCVRVJ1pQEwd/WFBUAlElCFBFUnlaB1ULByRdBEFdfVtWVA==` - checksum `7fd7dd1d0e959f74c133c13abb740b9faa61ab06bd0ecd177645e93b1e3825dd` > https://cyberchef.org/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true,false)XOR(%7B'option':'UTF8','string':'FlareOn2024'%7D,'Standard',false)&input=Y1FvRlJRRXJYMVlBVncxelZRZEZVU3hmQVFOUkJYVU5BeEJTZTE1UUNWUlZKMXBRRXdkL1dGQlVBbEVsQ0ZCRlVubGFCMVVMQnlSZEJFRmRmVnRXVkE9PQ - 給出對的 checksum 會用 chacha20 把一塊資料解密出 JPEG 圖片寫入 `%userprofile%/AppData/Local/REAL_FLAREON_FLAG.JPG` ![REAL_FLAREON_FLAG](https://hackmd.io/_uploads/ry6s2xkG1g.jpg) - reference - https://pkg.go.dev/golang.org/x/crypto@v0.27.0/chacha20poly1305#pkg-overview ## Challenge 3 - aray: yara > filename: `aray.yara` > filetype: `txt` > estimated time: 1 hour ![image](https://hackmd.io/_uploads/BJy8a4KCR.png) - 題目敘述 ``` And now for something completely different. I'm pretty sure you know how to write Yara rules, but can you reverse them? ``` - 解題經歷 逆向 yara rule, 回推出可以被這條規則偵測的 85 bytes 字串, 評估拿 hex editor 手拼花不到一個小時, 就不寫程式了 - 計算過程 - first byte ![image](https://hackmd.io/_uploads/BkD4RNKRA.png) - hash cracking ``` Line 37: hash.crc32(8, 2) == 0x61089c5c and Line 43: hash.crc32(34, 2) == 0x5888fc1b and Line 152: hash.crc32(63, 2) == 0x66715919 and Line 166: hash.sha256(14, 2) == "403d5f23d149670348b147a15eeb7010914701a7e99aad2e43f90cfa0325c76f" and Line 232: hash.sha256(56, 2) == "593f2d04aab251f60c9e4b8bbc1e05a34e920980ec08351a18459b2bc7dbf2f6" and Line 326: hash.md5(0, 2) == "89484b14b36a8d5329426a3d944d2983" and Line 332: hash.crc32(78, 2) == 0x7cab8d64 and Line 381: hash.md5(76, 2) == "f98ed07a4d5f50f7de1410d905f1477f" and Line 457: hash.md5(50, 2) == "657dae0913ee12be6fb2a6f687aae1c7" and Line 498: hash.md5(32, 2) == "738a656e8e8ec272ca17cd51e12f558b" and ``` ``` 593f2d04aab251f60c9e4b8bbc1e05a34e920980ec08351a18459b2bc7dbf2f6:fl 657dae0913ee12be6fb2a6f687aae1c7:3A 738a656e8e8ec272ca17cd51e12f558b:ul 89484b14b36a8d5329426a3d944d2983:ru f98ed07a4d5f50f7de1410d905f1477f:io 403d5f23d149670348b147a15eeb7010914701a7e99aad2e43f90cfa0325c76f: s ``` ```python import zlib import itertools def crack_crc(target): for byte1 in range(256): for byte2 in range(256): data = bytes([byte1, byte2]) crc32_value = zlib.crc32(data) & 0xFFFFFFFF if crc32_value == target: return data print(crack_crc(0x61089c5c)) # re print(crack_crc(0x5888fc1b)) # eA print(crack_crc(0x66715919)) # n. print(crack_crc(0x7cab8d64)) # n: ``` - simple math ``` Line 8: filesize == 85 and Line 10: uint8(58) + 25 == 122 and Line 15: uint32(52) ^ 425706662 == 1495724241 and Line 29: uint32(17) - 323157430 == 1412131772 and Line 37: uint8(36) + 4 == 72 and Line 56: uint8(27) ^ 21 == 40 and Line 78: uint32(59) ^ 512952669 == 1908304943 and Line 91: uint8(65) - 29 == 70 and Line 108: uint8(45) ^ 9 == 104 and Line 140: uint32(28) - 419186860 == 959764852 and Line 141: uint8(74) + 11 == 116 and Line 244: uint8(75) - 30 == 86 and Line 249: uint32(66) ^ 310886682 == 849718389 and Line 251: uint32(10) + 383041523 == 2448764514 and Line 280: uint32(37) + 367943707 == 1228527996 and Line 286: uint32(22) ^ 372102464 == 1879700858 and Line 313: uint8(2) + 11 == 119 and Line 322: uint32(46) - 412326611 == 1503714457 and Line 355: uint8(7) - 15 == 82 and Line 358: uint32(70) + 349203301 == 2034162376 and Line 390: uint32(80) - 473886976 == 69677856 and Line 411: uint32(3) ^ 298697263 == 2108416586 and Line 412: uint8(21) - 21 == 94 and Line 417: uint8(16) ^ 7 == 115 and Line 437: uint32(41) + 404880684 == 1699114335 and Line 458: uint8(26) - 7 == 25 and Line 510: uint8(84) + 3 == 128 and ``` - 結果 ``` rule flareon { strings: $f = "1RuleADayK33p$Malw4r3Aw4y@flare-on.com" condition: $f } ``` ## Challenge 4 - mememaker3000: javascript > filename: `mememaker3000.html` > filetype: `javascript` > estimated time: 1~2 hour - 題目敘述 ``` You've made it very far, I'm proud of you even if noone else is. You've earned yourself a break with some nice HTML and JavaScript before we get into challenges that may require you to be very good at computers. ``` - 解題經歷 html 裡包含一個經過混淆的 javascript, 解的時候是用 vscode + 瀏覽器 Console 硬幹, 發現跳 flag 邏輯就在 a0k 裡, 事後看官方解答發現有工具 Obfuscator.io Deobfuscator - 逆向工程 - a0k - get flag ```javascript function a0k() { const a = a0g['alt']['split']('/')['pop'](); if (a !== Object['keys'](meme_image)[0x5]) return; // a should be boy_friend0.jpg const b = a0l['textContent'], // should be "FLARE On" c = a0m['textContent'], // should be "Security Expert" d = a0n['textContent']; // should be "Malware" if (a0c['indexOf'](b) == 0xe && a0c['indexOf'](c) == a0c['length'] - 0x1 && a0c['indexOf'](d) == 0x16) { var e = new Date()['getTime'](); while (new Date()['getTime']() < e + 0xbb8) {} var f = d[0x3] + 'h' + a[0xa] + b[0x2] + a[0x3] + c[0x5] + c[c['length'] - 0x1] + '5' + a[0x3] + '4' + a[0x3] + c[0x2] + c[0x4] + c[0x3] + '3' + d[0x2] + a[0x3] + 'j4' + a0c[0x1][0x2] + d[0x4] + '5' + c[0x2] + d[0x5] + '1' + c[0xb] + '7' + a0c[0x15][0x1] + b[a0b(0x15e39) + 'e']('\x20', '-') + a[0xb] + a0c[0x4][a0b(0x9a82) + a0b(0x1656b)](0xc, 0xf); f = f['toLowerCase'](), alert(atob('Q29uZ3JhdHVsYXRpb25zISBIZXJlIHlvdSBnbzog') + f); } } ``` ![image](https://hackmd.io/_uploads/rJ-ybHqRC.png) - boy_friend0.jpg ![image](https://hackmd.io/_uploads/H1NzwScAC.png) - 結果 - solve (execute in console) ``` const a = 'boy_friend0.jpg', b = 'FLARE On', c = 'Security Expert', d = 'Malware'; var f = d[0x3] + 'h' + a[0xa] + b[0x2] + a[0x3] + c[0x5] + c[c['length'] - 0x1] + '5' + a[0x3] + '4' + a[0x3] + c[0x2] + c[0x4] + c[0x3] + '3' + d[0x2] + a[0x3] + 'j4' + a0c[0x1][0x2] + d[0x4] + '5' + c[0x2] + d[0x5] + '1' + c[0xb] + '7' + a0c[0x15][0x1] + b[a0b(0x15e39) + 'e']('\x20', '-') + a[0xb] + a0c[0x4][a0b(0x9a82) + a0b(0x1656b)](0xc, 0xf); f = f['toLowerCase'](), alert(atob('Q29uZ3JhdHVsYXRpb25zISBIZXJlIHlvdSBnbzog') + f); ``` ![image](https://hackmd.io/_uploads/BkNilU9AC.png) - tool - obfuscator.io - https://github.com/javascript-obfuscator/javascript-obfuscator - - obfuscated.io deobfuscator - https://github.com/ben-sb/obfuscator-io-deobfuscator - https://obf-io.deobfuscate.io/ ## Challenge 5 - sshd: coredump > filename: `ssh_container.tar` > filetype: `docker image` > estimated time: ?? > real time: 8 days - 題目敘述 ``` Our server in the FLARE Intergalactic HQ has crashed! Now criminals are trying to sell me my own data!!! Do your part, random internet hacker, to help FLARE out and tell us what data they stole! We used the best forensic preservation technique of just copying all the files on the system for you. ``` - 解題經歷 這題給了一個 docker container dump, 一開始找分析目標 (coredump) 就花了不少時間, 打開 coredump 看了一下, 發現問題是出在 `/lib/x86_64-linux-gnu/liblzma.so.5` 這個函式庫, 這個函式庫經過篡改(重裝了一個一樣版本的 docker 及 sshd, 比對檔案 hash), 分析 coredump 中指出這個函式庫的問題點, 發現這裡包含一個解密 shellcode -> 執行shellcode -> 加密 shellcode 的邏輯, 以 coredump 中殘留的 chacha20 key 跟 nonce 解出 shellcode, shellcode 裡面執行了回報中繼站、讀檔、加密等行為, 加密也是用 chacha20, 但有偷改加密參數 `expand 32-byte K` (原標準 chacha20 是 `expand 32-byte k`), 花了兩天的時間才發現== ### docker forensics - import docker image ``` docker import .\ssh_container.tar docker run -it ssh_container:latest bash docker run --init --ulimit core=-1 --security-opt seccomp=unconfined -it ssh_container:latest bash ``` ![image](https://hackmd.io/_uploads/SJtNwY9AA.png) - lastb ``` root@3bb39ed25370:~# lastb remnux ssh:notty 172.17.0.1 Tue Jul 30 22:08 - 22:08 (00:00) remnux ssh:notty 172.17.0.1 Tue Jul 30 22:08 - 22:08 (00:00) btmp begins Tue Jul 30 22:08:53 2024 ``` - added user ![image](https://hackmd.io/_uploads/r1GjKt9CC.png) - apt log ``` cat var/log/apt/history.log | grep 'Start-Date:' Start-Date: 2024-07-30 21:23:22 Commandline: apt-get install openssh-server -- Start-Date: 2024-07-30 22:22:39 Commandline: apt-get install build-essential -- Start-Date: 2024-07-30 22:36:08 Commandline: apt-get install ltrace -- Start-Date: 2024-07-31 19:14:55 Commandline: apt-get install vim -- Start-Date: 2024-07-31 22:18:10 Commandline: apt-get install iputils-ping -- Start-Date: 2024-09-09 21:21:50 Commandline: apt-get install gdb ``` - search by timeline ``` find / -type f -newermt "2024-09-09 21:00" ! -newermt "2024-09-09 21:55" | grep -v 'mime' ... /var/lib/systemd/coredump/sshd.core.93794.0.0.11.1725917676 ... ``` - something weird - same version sshd `OpenSSH_9.2p1 Debian-2+deb12u3, OpenSSL 3.0.14 4 Jun 2024` with different hash ![image](https://hackmd.io/_uploads/BysWgxJkJe.png) ![image](https://hackmd.io/_uploads/rkeVex1yyl.png) ``` $ cmp -l sshd/usr/sbin/sshd baseline/sshd | awk '{printf "%08X %02X %02X\n", $1, strtonum(0$2), strtonum(0$3)}' 0005AF71 C3 41 000C33F1 C3 53 ``` - 0005AF71 (55B46C733F71) ![image](https://hackmd.io/_uploads/SJ28hl1y1g.png) - 000C33F1 (55B46C79C3F1) ![image](https://hackmd.io/_uploads/Hyq43lJJJe.png) - different library hash ![image](https://hackmd.io/_uploads/S1GrxekkJx.png) - lz version in victim ``` root@b74702cbbdeb:/# xz --version xz (XZ Utils) 5.4.1 liblzma 5.6.2 ``` - sshd config - victim has added one line ``` ... UsePrivilegeSeparation no ``` ### debugging coredump - sshd source - https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-9.2p1.tar.gz - gdb ``` gdb /usr/sbin/sshd /var/lib/systemd/coredump/sshd.core.93794.0.0.11.1725917676 ``` - gdb with debuginfo ``` apt install debuginfod export DEBUGINFOD_URLS="https://debuginfod.debian.net/" ``` - backtrace ``` (gdb) backtrace #0 0x0000000000000000 in ?? () #1 0x00007f4a18c8f88f in ?? () from /lib/x86_64-linux-gnu/liblzma.so.5 #2 0x000055b46c7867c0 in ?? () #3 0x000055b46c73f9d7 in ?? () #4 0x000055b46c73ff80 in ?? () #5 0x000055b46c71376b in ?? () #6 0x000055b46c715f36 in ?? () #7 0x000055b46c7199e0 in ?? () #8 0x000055b46c6ec10c in ?? () #9 0x00007f4a18e5824a in __libc_start_call_main (main=main@entry=0x55b46c6e7d50, argc=argc@entry=4, argv=argv@entry=0x7ffcc6602eb8) at ../sysdeps/nptl/libc_start_call_main.h:58 #10 0x00007f4a18e58305 in __libc_start_main_impl (main=0x55b46c6e7d50, argc=4, argv=0x7ffcc6602eb8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffcc6602ea8) at ../csu/libc-start.c:360 #11 0x000055b46c6ec621 in ?? () ``` - after loading symbols ``` (gdb) backtrace #0 0x0000000000000000 in ?? () #1 0x00007f4a18c8f88f in ?? () from /lib/x86_64-linux-gnu/liblzma.so.5 #2 0x000055b46c7867c0 in openssh_RSA_verify (rsa=0x55b46d58e080, siglen=<optimized out>, sigbuf=<optimized out>, hashlen=<optimized out>, hash=0x7ffcc6602020 "\317\301\b\021F\016\3367\250\223j\342\345\303~\356O\"\370,\032=Y%r\247\311\324n\263z\303\037\225\342\0335\375}\360\242\200&,\254(\271\354\252n\200\300\245\326:u{&K{?+NlP\316Xm\264U", hash_alg=4) at ../../ssh-rsa.c:660 #3 ssh_rsa_verify (key=<optimized out>, sig=<optimized out>, siglen=<optimized out>, data=<optimized out>, dlen=<optimized out>, alg=<optimized out>, compat=0, detailsp=0x0) at ../../ssh-rsa.c:552 #4 0x000055b46c73f9d7 in cert_parse (certbuf=0x55b46d58c300, key=0x55b46d58c350, b=0x55b46d58c230) at ../../sshkey.c:1867 #5 sshkey_from_blob_internal (b=0x55b46d58c230, keyp=keyp@entry=0x7ffcc66021b8, allow_cert=allow_cert@entry=1) at ../../sshkey.c:1942 #6 0x000055b46c73ff80 in sshkey_froms (buf=buf@entry=0x55b46d58c1e0, keyp=keyp@entry=0x7ffcc66021b8) at ../../sshkey.c:1988 #7 0x000055b46c71376b in mm_answer_keyallowed (ssh=0x55b46d57a100, sock=5, m=0x55b46d58c1e0) at ../../monitor.c:1206 #8 0x000055b46c715f36 in monitor_read (ssh=ssh@entry=0x55b46d57a100, pmonitor=pmonitor@entry=0x55b46d56ecb0, ent=0x55b46c80d320 <mon_dispatch_proto20+224>, pent=pent@entry=0x7ffcc66022b0) at ../../monitor.c:535 #9 0x000055b46c7199e0 in monitor_child_preauth (ssh=ssh@entry=0x55b46d57a100, pmonitor=0x55b46d56ecb0) at ../../monitor.c:316 #10 0x000055b46c6ec10c in privsep_preauth (ssh=0x55b46d57a100) at ../../sshd.c:519 #11 main (ac=<optimized out>, av=<optimized out>) at ../../sshd.c:2336 ``` - proc mappings ``` (gdb) info proc mappings Mapped address spaces: Start Addr End Addr Size Offset objfile 0x55b46c6d9000 0x55b46c6e6000 0xd000 0x0 /usr/sbin/sshd 0x55b46c6e6000 0x55b46c7b4000 0xce000 0xd000 /usr/sbin/sshd 0x55b46c7b4000 0x55b46c809000 0x55000 0xdb000 /usr/sbin/sshd 0x55b46c809000 0x55b46c80d000 0x4000 0x12f000 /usr/sbin/sshd 0x55b46c80d000 0x55b46c80e000 0x1000 0x133000 /usr/sbin/sshd 0x7f4a188a2000 0x7f4a188a3000 0x1000 0x0 /usr/lib/x86_64-linux-gnu/security/pam_env.so 0x7f4a188a3000 0x7f4a188a5000 0x2000 0x1000 /usr/lib/x86_64-linux-gnu/security/pam_env.so 0x7f4a188a5000 0x7f4a188a6000 0x1000 0x3000 /usr/lib/x86_64-linux-gnu/security/pam_env.so 0x7f4a188a6000 0x7f4a188a7000 0x1000 0x3000 /usr/lib/x86_64-linux-gnu/security/pam_env.so 0x7f4a188a7000 0x7f4a188a8000 0x1000 0x4000 /usr/lib/x86_64-linux-gnu/security/pam_env.so 0x7f4a188a8000 0x7f4a188aa000 0x2000 0x0 /usr/lib/x86_64-linux-gnu/security/pam_limits.so 0x7f4a188aa000 0x7f4a188ad000 0x3000 0x2000 /usr/lib/x86_64-linux-gnu/security/pam_limits.so 0x7f4a188ad000 0x7f4a188ae000 0x1000 0x5000 /usr/lib/x86_64-linux-gnu/security/pam_limits.so 0x7f4a188ae000 0x7f4a188af000 0x1000 0x6000 /usr/lib/x86_64-linux-gnu/security/pam_limits.so 0x7f4a188af000 0x7f4a188b0000 0x1000 0x7000 /usr/lib/x86_64-linux-gnu/security/pam_limits.so 0x7f4a188b0000 0x7f4a188b1000 0x1000 0x0 /usr/lib/x86_64-linux-gnu/security/pam_mail.so 0x7f4a188b1000 0x7f4a188b2000 0x1000 0x1000 /usr/lib/x86_64-linux-gnu/security/pam_mail.so 0x7f4a188b2000 0x7f4a188b3000 0x1000 0x2000 /usr/lib/x86_64-linux-gnu/security/pam_mail.so 0x7f4a188b3000 0x7f4a188b4000 0x1000 0x2000 /usr/lib/x86_64-linux-gnu/security/pam_mail.so 0x7f4a188b4000 0x7f4a188b5000 0x1000 0x3000 /usr/lib/x86_64-linux-gnu/security/pam_mail.so 0x7f4a188b5000 0x7f4a188c5000 0x10000 0x0 /usr/lib/x86_64-linux-gnu/libm.so.6 0x7f4a188c5000 0x7f4a18938000 0x73000 0x10000 /usr/lib/x86_64-linux-gnu/libm.so.6 0x7f4a18938000 0x7f4a18992000 0x5a000 0x83000 /usr/lib/x86_64-linux-gnu/libm.so.6 0x7f4a18992000 0x7f4a18993000 0x1000 0xdc000 /usr/lib/x86_64-linux-gnu/libm.so.6 0x7f4a18993000 0x7f4a18994000 0x1000 0xdd000 /usr/lib/x86_64-linux-gnu/libm.so.6 ... 0x7f4a18c86000 0x7f4a18c8a000 0x4000 0x0 / (deleted) 0x7f4a18c8a000 0x7f4a18ca9000 0x1f000 0x4000 / (deleted) 0x7f4a18ca9000 0x7f4a18cb7000 0xe000 0x23000 / (deleted) 0x7f4a18cb7000 0x7f4a18cb8000 0x1000 0x30000 / (deleted) 0x7f4a18cb8000 0x7f4a18cb9000 0x1000 0x31000 / (deleted) ... ``` - registers ``` (gdb) info registers rax 0x0 0 rbx 0x1 1 rcx 0x55b46d58e080 94233417015424 rdx 0x55b46d58eb20 94233417018144 rsi 0x55b46d51dde0 94233416556000 rdi 0x200 512 rbp 0x55b46d51dde0 0x55b46d51dde0 rsp 0x7ffcc6601e98 0x7ffcc6601e98 r8 0x1 1 r9 0x7ffcc6601e10 140723636674064 r10 0x1e 30 r11 0x7d63ee63 2103701091 r12 0x200 512 r13 0x55b46d58eb20 94233417018144 r14 0x55b46d58e080 94233417015424 r15 0x7ffcc6601ec0 140723636674240 rip 0x0 0x0 eflags 0x10206 [ PF IF RF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0 ``` - rsp ``` (gdb) x/16xg $rsp 0x7ffcc6601e98: 0x00007f4a18c8f88f 0x000055b46d58df60 0x7ffcc6601ea8: 0x00007f4a188a1000 0x000055b46d51dde4 0x7ffcc6601eb8: 0x000055b46d51de04 0x7cd703ae8b2ff828 0x7ffcc6601ec8: 0x67711ce280e2582c 0x0be691bcde9b3c23 0x7ffcc6601ed8: 0x034c0c188bde1fac 0xe479508bfe7ad8d6 0x7ffcc6601ee8: 0x2331758100073bf5 0x505e2d16722f862c 0x7ffcc6601ef8: 0x291ce37558aa8b9f 0x0000000000000016 0x7ffcc6601f08: 0xe21318a838f63d94 0xbaa0f907a51863de ``` - frame 1 ![image](https://hackmd.io/_uploads/HJcKjUCCR.png) ``` #1 0x00007f4a18c8f88f in ?? () from /lib/x86_64-linux-gnu/liblzma.so.5 ... (gdb) print $edi $2 = 512 (gdb) print *(RSA *)$rcx $1 = {dummy_zero = 0, libctx = 0x0, version = 0, meth = 0x7f4a194b4260 <rsa_pkcs1_ossl_meth>, engine = 0x0, n = 0x55b46d58e1f0, e = 0x55b46d58e1b0, d = 0x0, p = 0x0, q = 0x0, dmp1 = 0x0, dmq1 = 0x0, iqmp = 0x0, pss_params = {hash_algorithm_nid = 0, mask_gen = {algorithm_nid = 0, hash_algorithm_nid = 0}, salt_len = 0, trailer_field = 0}, pss = 0x0, prime_infos = 0x0, ex_data = {ctx = 0x0, sk = 0x0}, references = 1, flags = 6, _method_mod_n = 0x0, _method_mod_p = 0x0, _method_mod_q = 0x0, blinding = 0x0, mt_blinding = 0x0, lock = 0x55b46d58e170, dirty_cnt = 1} ``` - https://docs.openssl.org/3.2/man3/RSA_private_encrypt/#synopsis - frame 2 ``` #2 0x000055b46c7867c0 in openssh_RSA_verify (rsa=0x55b46d58e080, siglen=<optimized out>, sigbuf=<optimized out>, hashlen=<optimized out>, hash=0x7ffcc6602020 "\317\301\b\021F\016\3367\250\223j\342\345\303~\356O\"\370,\032=Y%r\247\311\324n\263z\303\037\225\342\0335\375}\360\242\200&,\254(\271\354\252n\200\300\245\326:u{&K{?+NlP\316Xm\264U", hash_alg=4) at ../../ssh-rsa.c:660 ... ``` ``` #define SSH_DIGEST_SHA512 4 ``` sha512: `cfc10811460ede37a8936ae2e5c37eee4f22f82c1a3d592572a7c9d46eb37ac31f95e21b35fd7df0a280262cac28b9ecaa6e80c0a5d63a757b264b7b3f2b4e6c` ### chacha20 decrypt 1 - got a shellcode - decrypt - ciphertext - key (coredump 55B46D51DDE4) `94 3D F6 38 A8 18 13 E2 DE 63 18 A5 07 F9 A0 BA 2D BB 8A 7B A6 36 66 D0 8D 11 A6 5E C9 14 D6 6F` - nonce (coredump 55B46D51DE04) `F2 36 83 9F 4D CD 71 1A 52 86 29 55` - counter 0 - decrypted result - shellcode file md5 `11c5a6eda8ffbcda6d618564e2938a4a` ### chacha20 decrypt 2 - get content of `/root/certificate_authority_signing_key.txt` - decrypt - ciphertext (coredump 7FFCC6600D18) `A9 F6 34 08 42 2A 9E 1C 0C 03 A8 08 94 70 BB 8D AA DC 6D 7B 24 FF 7F 24 7C DA 83 9E 92 F7 07 1D 02 63 90 2E C1 58` - key (coredump 7FFCC6600BE8) `8D EC 91 12 EB 76 0E DA 7C 7D 87 A4 43 27 1C 35 D9 E0 CB 87 89 93 B4 D9 04 AE F9 34 FA 21 66 D7` - nonce (7FFCC6600C08) `11 11 11 11 11 11 11 11 11 11 11 11` - counter 0 - TODO - failed - https://cyberchef.org/#recipe=ChaCha(%7B'option':'Hex','string':'8D%20EC%2091%2012%20EB%2076%200E%20DA%207C%207D%2087%20A4%2043%2027%201C%2035%20D9%20E0%20CB%2087%2089%2093%20B4%20D9%2004%20AE%20F9%2034%20FA%2021%2066%20D7'%7D,%7B'option':'Hex','string':'11%2011%2011%2011%2011%2011%2011%2011%2011%2011%2011%2011'%7D,0,'20','Hex','Hex')From_Hex('Auto')&input=QTkgRjYgMzQgMDggNDIgMkEgOUUgMUMgMEMgMDMgQTggMDggOTQgNzAgQkIgOEQgQUEgREMgNkQgN0IgMjQgRkYgN0YgMjQgN0MgREEgODMgOUUgOTIgRjcgMDcgMUQgMDIgNjMgOTAgMkUgQzEgNTg - key point ![image](https://hackmd.io/_uploads/SyFN7zHJJx.png) - solve.py ```python from binascii import hexlify import struct chacha20_constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574] # "expand 32-byte k" chacha20_constants1 = [0x61707865, 0x3320646e, 0x79622d32, 0x4b206574] # "expand 32-byte K" # Rotates the bits of the number x left by n positions def rotate_left(x, n): return ((x << n) & 0xFFFFFFFF) | (x >> (32 - n)) # ChaCha20 quarter round function def quarter_round(state, a, b, c, d): state[a] = (state[a] + state[b]) & 0xFFFFFFFF state[d] ^= state[a] state[d] = rotate_left(state[d], 16) state[c] = (state[c] + state[d]) & 0xFFFFFFFF state[b] ^= state[c] state[b] = rotate_left(state[b], 12) state[a] = (state[a] + state[b]) & 0xFFFFFFFF state[d] ^= state[a] state[d] = rotate_left(state[d], 8) state[c] = (state[c] + state[d]) & 0xFFFFFFFF state[b] ^= state[c] state[b] = rotate_left(state[b], 7) # ChaCha20 block function def chacha20_block1(key, counter, nonce): # Constants constants = chacha20_constants1 # Initialize the state state = constants + list(struct.unpack('<8L', key)) + [counter] + list(struct.unpack('<3L', nonce)) # Copy the state for use in the final addition step working_state = state[:] # Perform 20 rounds (10 iterations of the double round) for _ in range(10): # Odd round quarter_round(working_state, 0, 4, 8, 12) quarter_round(working_state, 1, 5, 9, 13) quarter_round(working_state, 2, 6, 10, 14) quarter_round(working_state, 3, 7, 11, 15) # Even round quarter_round(working_state, 0, 5, 10, 15) quarter_round(working_state, 1, 6, 11, 12) quarter_round(working_state, 2, 7, 8, 13) quarter_round(working_state, 3, 4, 9, 14) # Add the original state to the working state output_state = [(working_state[i] + state[i]) & 0xFFFFFFFF for i in range(16)] # Convert the state to bytes return struct.pack('<16L', *output_state) def chacha20_block2(key, counter, nonce): # Constants constants = chacha20_constants1 #constants = [0x65787061, 0x6e642033, 0x322d6279, 0x7465206b] # Initialize the state state = constants + list(struct.unpack('<8L', key)) + [counter] + list(struct.unpack('<3L', nonce)) # Copy the state for use in the final addition step working_state = state[:] # Perform 20 rounds (10 iterations of the double round) for _ in range(10): # Odd round # First quarter round (0, 4, 8, 12) working_state[0] = (working_state[0] + working_state[4]) & 0xFFFFFFFF working_state[12] ^= working_state[0] working_state[12] = rotate_left(working_state[12], 16) working_state[8] = (working_state[8] + working_state[12]) & 0xFFFFFFFF working_state[4] ^= working_state[8] working_state[4] = rotate_left(working_state[4], 12) working_state[0] = (working_state[0] + working_state[4]) & 0xFFFFFFFF working_state[12] ^= working_state[0] working_state[12] = rotate_left(working_state[12], 8) working_state[8] = (working_state[8] + working_state[12]) & 0xFFFFFFFF working_state[4] ^= working_state[8] working_state[4] = rotate_left(working_state[4], 7) # Second quarter round (1, 5, 9, 13) working_state[1] = (working_state[1] + working_state[5]) & 0xFFFFFFFF working_state[13] ^= working_state[1] working_state[13] = rotate_left(working_state[13], 16) working_state[9] = (working_state[9] + working_state[13]) & 0xFFFFFFFF working_state[5] ^= working_state[9] working_state[5] = rotate_left(working_state[5], 12) working_state[1] = (working_state[1] + working_state[5]) & 0xFFFFFFFF working_state[13] ^= working_state[1] working_state[13] = rotate_left(working_state[13], 8) working_state[9] = (working_state[9] + working_state[13]) & 0xFFFFFFFF working_state[5] ^= working_state[9] working_state[5] = rotate_left(working_state[5], 7) # Third quarter round (2, 6, 10, 14) working_state[2] = (working_state[2] + working_state[6]) & 0xFFFFFFFF working_state[14] ^= working_state[2] working_state[14] = rotate_left(working_state[14], 16) working_state[10] = (working_state[10] + working_state[14]) & 0xFFFFFFFF working_state[6] ^= working_state[10] working_state[6] = rotate_left(working_state[6], 12) working_state[2] = (working_state[2] + working_state[6]) & 0xFFFFFFFF working_state[14] ^= working_state[2] working_state[14] = rotate_left(working_state[14], 8) working_state[10] = (working_state[10] + working_state[14]) & 0xFFFFFFFF working_state[6] ^= working_state[10] working_state[6] = rotate_left(working_state[6], 7) # Fourth quarter round (3, 7, 11, 15) working_state[3] = (working_state[3] + working_state[7]) & 0xFFFFFFFF working_state[15] ^= working_state[3] working_state[15] = rotate_left(working_state[15], 16) working_state[11] = (working_state[11] + working_state[15]) & 0xFFFFFFFF working_state[7] ^= working_state[11] working_state[7] = rotate_left(working_state[7], 12) working_state[3] = (working_state[3] + working_state[7]) & 0xFFFFFFFF working_state[15] ^= working_state[3] working_state[15] = rotate_left(working_state[15], 8) working_state[11] = (working_state[11] + working_state[15]) & 0xFFFFFFFF working_state[7] ^= working_state[11] working_state[7] = rotate_left(working_state[7], 7) # Even round # First quarter round (0, 5, 10, 15) working_state[0] = (working_state[0] + working_state[5]) & 0xFFFFFFFF working_state[15] ^= working_state[0] working_state[15] = rotate_left(working_state[15], 16) working_state[10] = (working_state[10] + working_state[15]) & 0xFFFFFFFF working_state[5] ^= working_state[10] working_state[5] = rotate_left(working_state[5], 12) working_state[0] = (working_state[0] + working_state[5]) & 0xFFFFFFFF working_state[15] ^= working_state[0] working_state[15] = rotate_left(working_state[15], 8) working_state[10] = (working_state[10] + working_state[15]) & 0xFFFFFFFF working_state[5] ^= working_state[10] working_state[5] = rotate_left(working_state[5], 7) # Second quarter round (1, 6, 11, 12) working_state[1] = (working_state[1] + working_state[6]) & 0xFFFFFFFF working_state[12] ^= working_state[1] working_state[12] = rotate_left(working_state[12], 16) working_state[11] = (working_state[11] + working_state[12]) & 0xFFFFFFFF working_state[6] ^= working_state[11] working_state[6] = rotate_left(working_state[6], 12) working_state[1] = (working_state[1] + working_state[6]) & 0xFFFFFFFF working_state[12] ^= working_state[1] working_state[12] = rotate_left(working_state[12], 8) working_state[11] = (working_state[11] + working_state[12]) & 0xFFFFFFFF working_state[6] ^= working_state[11] working_state[6] = rotate_left(working_state[6], 7) # Third quarter round (2, 7, 8, 13) working_state[2] = (working_state[2] + working_state[7]) & 0xFFFFFFFF working_state[13] ^= working_state[2] working_state[13] = rotate_left(working_state[13], 16) working_state[8] = (working_state[8] + working_state[13]) & 0xFFFFFFFF working_state[7] ^= working_state[8] working_state[7] = rotate_left(working_state[7], 12) working_state[2] = (working_state[2] + working_state[7]) & 0xFFFFFFFF working_state[13] ^= working_state[2] working_state[13] = rotate_left(working_state[13], 8) working_state[8] = (working_state[8] + working_state[13]) & 0xFFFFFFFF working_state[7] ^= working_state[8] working_state[7] = rotate_left(working_state[7], 7) # Fourth quarter round (3, 4, 9, 14) working_state[3] = (working_state[3] + working_state[4]) & 0xFFFFFFFF working_state[14] ^= working_state[3] working_state[14] = rotate_left(working_state[14], 16) working_state[9] = (working_state[9] + working_state[14]) & 0xFFFFFFFF working_state[4] ^= working_state[9] working_state[4] = rotate_left(working_state[4], 12) working_state[3] = (working_state[3] + working_state[4]) & 0xFFFFFFFF working_state[14] ^= working_state[3] working_state[14] = rotate_left(working_state[14], 8) working_state[9] = (working_state[9] + working_state[14]) & 0xFFFFFFFF working_state[4] ^= working_state[9] working_state[4] = rotate_left(working_state[4], 7) # Add the original state to the working state output_state = [(working_state[i] + state[i]) & 0xFFFFFFFF for i in range(16)] # Convert the state to bytes return struct.pack('<16L', *output_state) # ChaCha20 encryption function def chacha20_encrypt1(key, counter, nonce, plaintext): key_stream = bytearray() ciphertext = bytearray() # Split plaintext into 64-byte blocks for i in range(0, len(plaintext), 64): block = plaintext[i:i + 64] key_stream_block = chacha20_block1(key, counter, nonce) # XOR the key stream with the plaintext for j in range(len(block)): ciphertext.append(block[j] ^ key_stream_block[j]) # Increment the counter counter += 1 return ciphertext # ChaCha20 encryption function def chacha20_encrypt2(key, counter, nonce, plaintext): key_stream = bytearray() ciphertext = bytearray() # Split plaintext into 64-byte blocks for i in range(0, len(plaintext), 64): block = plaintext[i:i + 64] key_stream_block = chacha20_block2(key, counter, nonce) # XOR the key stream with the plaintext for j in range(len(block)): ciphertext.append(block[j] ^ key_stream_block[j]) # Increment the counter counter += 1 return ciphertext # Example key and nonce key = bytes.fromhex('8DEC9112EB760EDA7C7D87A443271C35D9E0CB878993B4D904AEF934FA2166D7') nonce = bytes.fromhex('111111111111111111111111') counter = 0 # Example plaintext plaintext = b"\xA9\xF6\x34\x08\x42\x2A\x9E\x1C\x0C\x03\xA8\x08\x94\x70\xBB\x8D\xAA\xDC\x6D\x7B\x24\xFF\x7F\x24\x7C\xDA\x83\x9E\x92\xF7\x07\x1D\x02\x63\x90\x2E\xC1\x58" ciphertext1 = chacha20_encrypt1(key, counter, nonce, plaintext) print(ciphertext1) ciphertext2 = chacha20_encrypt2(key, counter, nonce, plaintext) print(ciphertext2) ``` ### References - https://github.com/tukaani-project/xz/issues/103 - https://www.rapid7.com/blog/post/2024/04/01/etr-backdoored-xz-utils-cve-2024-3094/ - https://snyk.io/blog/the-xz-backdoor-cve-2024-3094/ - https://medium.com/@Jackiesogi/liblzma-cve-2024-3094-%E6%BC%8F%E6%B4%9E%E7%B0%A1%E4%BB%8B-6d875e59ea1f - https://en.wikipedia.org/wiki/Salsa20 - Frame Pointer Omission (FPO) Optimization ![image](https://hackmd.io/_uploads/SyGO2bZJJg.png) ## Challenge 6 - bloke2: verilog, blake2 > filename: `bloke2` > filetype: `verilog source codes` > estimated time: 3 min > real time: 3 day - 題目敘述 ``` You've been so helpful lately, and that was very good work you did. Yes, I'm going to put it right here, on the refrigerator, very good job indeed. You're the perfect person to help me with another issue that come up. One of our lab researchers has mysteriously disappeared. He was working on the prototype for a hashing IP block that worked very much like, but not identically to, the common Blake2 hash family. Last we heard from him, he was working on the testbenches for the unit. One of his labmates swears she knew of a secret message that could be extracted with the testbenches, but she couldn't quite recall how to trigger it. Maybe you could help? ``` - 解題經歷 這題是一個 Verilog 專案, 稍微看了一下程式碼, 發現裡面有一些有趣的參數跟 blake2 雜湊演算法有關, 執行之後發現雜湊結果跟正常的結果差蠻多的, 而且只能 hash 63/127 bytes 的 message, 再長結果就會一樣, 就在想是不是要跟上一題一樣找出與標準實作的差異, 猜劇本是可碰撞或可逆, 也花了好幾天幾乎快實作出一個變形的 blake2 hash, 在比對之後發現這支 Verilog 專案實作的 blake2 核心功能大部分都一樣, 雖然沒有做得很標準, 但應該沒有碰撞或可逆的空間, 就仔細回去看題目, 才發現只要修改專案, 把 TEST_VAL 那邊註解掉直接跑 testbench 就可以了(了解題目想幹麼也是非常的重要!) - 線索 - clue 1 ![image](https://hackmd.io/_uploads/HJxTZGTJkl.png) - clue 2 ![image](https://hackmd.io/_uploads/SJoF-GpJyg.png) - clue 3 XOR operation ![image](https://hackmd.io/_uploads/S1pHGMay1x.png) ![image](https://hackmd.io/_uploads/SJCSgf6yJx.png) ### Others - Background - README.md ![image](https://hackmd.io/_uploads/By2EufB1Jl.png) - background - blake2b (512) - blake2s (256) - sha2 initial hash value - reference: https://en.wikipedia.org/wiki/SHA-2 - in bloke2b.v - sha512 ``` .IV(512'h6A09E667F3BCC908BB67AE8584CAA73B3C6EF372FE94F82BA54FF53A5F1D36F1510E527FADE682D19B05688C2B3E6C1F1F83D9ABFB41BD6B5BE0CD19137E2179) ``` - in bloke2s.v - sha256 ``` .IV(256'h6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19) ``` - run the code ``` sudo apt update sudo apt install iverilog make make tests ``` - test `make tests` (modify `%s` to `%x` in `$display("Received message: %x", message);`) ``` iverilog -g2012 -o f_sched.test.out f_sched.v f_sched_tb.v vvp f_sched.test.out iverilog -g2012 -o bloke2b.test.out bloke2.v f_sched.v f_unit.v g_over_2.v g.v g_unit.v data_mgr.v bloke2s.v bloke2b.v bloke2b_tb.v vvp bloke2b.test.out Received message: 37d68109da41fb2695333737ecd653b9f1339ef00cacd9e4329baad32645e67d273cd7f5e2597c4eecc43a279bd49fc5ca2cd98baee8099136904105e6e64336 Received message: 18bd715d0f0f16167938ccc242a0e179a3fb233e5b8be47169dc8a775267fd10923abd92ce30b7e2a0da1a0fb724450ecb05978d77e8354355e6e04d2dcfc0d7 Received message: 30d2cf2b11287333b4bc19f4d0db88169fb32770c9a3db24967667c81e78a8d39cefbe43b15fd56a6595fa89869fde30febb1533796dea268a6841b18393f351 iverilog -g2012 -o bloke2s.test.out bloke2.v f_sched.v f_unit.v g_over_2.v g.v g_unit.v data_mgr.v bloke2s.v bloke2b.v bloke2s_tb.v vvp bloke2s.test.out Received message: 0000000000000000000000000000000000000000000000000000000000000000c1f4134633d0c0708c8db5d9a8fa708d7b33784de22503c1c8e83d57abc2ec20 Received message: 00000000000000000000000000000000000000000000000000000000000000006ea2fe2803fb1e09f90005da46b472aa63e193c4406c75f09773c266460e7672 Received message: 00000000000000000000000000000000000000000000000000000000000000000c9d03a40f610224cd61e362a2ae7dacacb85cc548acbc4f3f77aa603fceb562 rm f_sched.test.out bloke2s.test.out bloke2b.test.out ``` - something interesting - bloke2b will take message length lower than 127 bytes / bloke2s lower than 63 bytes ``` # bloke2s_tb.v hash_message("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // len=62 hash_message("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // len=63 hash_message("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // len=64 Received message: 0000000000000000000000000000000000000000000000000000000000000000dd13dcd91301072a3981052845185456fdca2fcc6a3f3b87e5977395e6beee9b Received message: 000000000000000000000000000000000000000000000000000000000000000060dcc93bdd129fcba8873f2b9977f9ed6d687246bc70cb90ee2b85d882666d75 Received message: 000000000000000000000000000000000000000000000000000000000000000060dcc93bdd129fcba8873f2b9977f9ed6d687246bc70cb90ee2b85d882666d75 # bloke2b_tb.v hash_message("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // len=126 hash_message("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // len=127 hash_message("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // len=128 Received message: 6c960dc172b83dcbced592dae94c9095667d3622c04f545258c5a20eb98561d1dc33a17ef423ff35b85820b4c819352255199aaf390255398fda72ac1750621b Received message: 83c991ffc30674512bb27f5e52a1c7e413254addf6280c1faa805f601976c1be8421b561e961b182a19b269a3c2270ae14f2ef4c9aaab34e7433715a95d0aa57 Received message: 83c991ffc30674512bb27f5e52a1c7e413254addf6280c1faa805f601976c1be8421b561e961b182a19b269a3c2270ae14f2ef4c9aaab34e7433715a95d0aa57 ``` ### blake2x implementation - reference: https://github.com/buggywhip/blake2_py/blob/master/blake2.py ### mybloke2x implementation (TODO) - TODO: make bloke2("123") work ```python # reference: https://github.com/buggywhip/blake2_py/blob/master/blake2.py import struct, binascii, copy from ctypes import * DBUG2 = False # True False MASK8BITS = 0xff MASK16BITS = 0xffff MASK32BITS = 0xffffffff MASK48BITS = 0xffffffffffff MASK64BITS = 0xffffffffffffffff #--------------------------------------------------------------- class BLAKE2(object): """ BLAKE2 is a base class for BLAKE2b and BLAKE2s """ sigma = [ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 ], [ 14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3 ], [ 11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4 ], [ 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8 ], [ 9, 0, 5, 7, 2, 4,10,15,14, 1,11,12, 6, 8, 3,13 ], [ 2,12, 6,10, 0,11, 8, 3, 4,13, 7, 5,15,14, 1, 9 ], [ 12, 5, 1,15,14,13, 4,10, 0, 7, 6, 3, 9, 2, 8,11 ], [ 13,11, 7,14,12, 1, 3, 9, 5, 0,15, 4, 8, 6, 2,10 ], [ 6,15,14, 9,11, 3, 0, 8,12, 2,13, 7, 1, 4,10, 5 ], [ 10, 2, 8, 4, 7, 6, 1, 5,15,11, 9,14, 3,12,13 ,0 ], [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 ], [ 14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3 ], ] # only 1st 10 rows are used by BLAKE2s # - - - - - - - - - - - - - - - - - - - - - - - - - - - def __init__(self, digest_size=0, **args): print(""" *********************************************** * You just instantiated a base class. Please * * instantiate either BLAKE2b or BLAKE2s. * *********************************************** """) raise Exception('base class instantiation') # - - - - - - - - - - - - - - - - - - - - - - - - - - - def _init(self, key=b''): assert len(key) <= self.KEYBYTES # load parameters P = self.PARAMS() P.F.digest_size = self.digest_size P.F.key_length = len(key) P.F.fanout = self.fanout P.F.depth = self.depth P.F.leaf_size = self.leaf_size P.F.node_offset_lo = self.node_offset & MASK32BITS P.F.node_offset_hi = self.node_offset >> 32 P.F.node_depth = self.node_depth P.F.inner_size = self.inner_size P.F.salt = (self.salt + (chr(0).encode())*(self.SALTBYTES-len(self.salt))) P.F.person = (self.person + (chr(0).encode())*(self.PERSONALBYTES-len(self.person))) if DBUG2: print('') fmt = '%0' + str(self.WORDBYTES*2) + 'x' for i in range(8): print(' %2d: %s' % (i*8, fmt % P.W[i])) self.h = [self.IV[i] ^ P.W[i] for i in range(8)] self.totbytes = 0 self.t = [0]*2 self.f = [0]*2 self.buflen = 0 self.buf = b'' self.finalized = False self.block_size = self.BLOCKBYTES if key: block = key + (chr(0).encode())*(self.BLOCKBYTES-len(key)) self.update(block) if self.data: self.update(self.data) # - - - - - - - - - - - - - - - - - - - - - - - - - - - def _compress(self, block): MASKBITS = self.MASKBITS WORDBITS = self.WORDBITS WORDBYTES = self.WORDBYTES IV = self.IV sigma = self.sigma ROT1 = self.ROT1 ROT2 = self.ROT2 ROT3 = self.ROT3 ROT4 = self.ROT4 WB_ROT1 = WORDBITS - ROT1 WB_ROT2 = WORDBITS - ROT2 WB_ROT3 = WORDBITS - ROT3 WB_ROT4 = WORDBITS - ROT4 # convert block (bytes) into 16 LE words m = struct.unpack_from('<16%s' % self.WORDFMT, bytes(block)) v = [0]*16 ''' v[ 0: 8] = self.h v[ 8:12] = IV[:4] v[12] = self.t[0] ^ IV[4] v[13] = self.t[1] ^ IV[5] v[14] = self.f[0] ^ IV[6] v[15] = self.f[1] ^ IV[7] ''' v[:8] = IV[::-1] print(m) print([hex(v[i]) for i in range(16)]) # g.v and g_over_2.v def G(a, b, c, d): # dereference v[] for another small speed improvement - g_unit.v va = v[a] vb = v[b] vc = v[c] vd = v[d] # g.v - g_over_2 g0 va = (va + vb + m0_sel) & MASKBITS w = vd ^ va vd = (w >> ROT1) | (w << (WB_ROT1)) & MASKBITS vc = (vc + vd) & MASKBITS w = vb ^ vc vb = (w >> ROT2) | (w << (WB_ROT2)) & MASKBITS # g.v - g_over_2 g1 va = (va + vb + m1_sel) & MASKBITS w = vd ^ va vd = (w >> ROT3) | (w << (WB_ROT3)) & MASKBITS vc = (vc + vd) & MASKBITS w = vb ^ vc vb = (w >> ROT4) | (w << (WB_ROT4)) & MASKBITS # re-reference v[] - g_unit.v v[a] = va v[b] = vb v[c] = vc v[d] = vd # time to ChaCha for r in range(self.ROUNDS): print("funit rnd", r, "sub 0", [hex(v[i]) for i in range(16)]) sr = sigma[r] m0_sel = m[sr[15]] m1_sel = m[sr[14]] G( 0, 4, 8, 12) print("funit rnd", r, "sub 1", [hex(v[i]) for i in range(16)]) m0_sel = m[sr[13]] m1_sel = m[sr[12]] G( 1, 5, 9, 13) print("funit rnd", r, "sub 2", [hex(v[i]) for i in range(16)]) m0_sel = m[sr[11]] m1_sel = m[sr[10]] G( 2, 6, 10, 14) print("funit rnd", r, "sub 3", [hex(v[i]) for i in range(16)]) m0_sel = m[sr[9]] m1_sel = m[sr[8]] G( 3, 7, 11, 15) print("funit rnd", r, "sub 4", [hex(v[i]) for i in range(16)]) m0_sel = m[sr[7]] m1_sel = m[sr[6]] G( 0, 5, 10, 15) print("funit rnd", r, "sub 5", [hex(v[i]) for i in range(16)]) m0_sel = m[sr[5]] m1_sel = m[sr[4]] G( 1, 6, 11, 12) print("funit rnd", r, "sub 6", [hex(v[i]) for i in range(16)]) m0_sel = m[sr[3]] m1_sel = m[sr[2]] G( 2, 7, 8, 13) # !!!! self.h = [v[i] ^ v[i+8] for i in range(8)] print("funit rnd", r, "sub 7", [hex(v[i]) for i in range(16)]) m0_sel = m[sr[1]] m1_sel = m[sr[0]] G( 3, 4, 9, 14) #self.h = [self.h[i] ^ v[i] ^ v[i+8] for i in range(8)] # - - - - - - - - - - - - - - - - - - - - - - - - - - - def update(self, data): assert self.finalized == False BLOCKBYTES = self.BLOCKBYTES datalen = len(data) dataptr = 0 while True: if len(self.buf) > BLOCKBYTES: self._increment_counter(BLOCKBYTES) self._compress(self.buf[:BLOCKBYTES]) self.buf = self.buf[BLOCKBYTES:] if dataptr <= datalen: self.buf += data[dataptr:dataptr + BLOCKBYTES] dataptr += BLOCKBYTES else: break # - - - - - - - - - - - - - - - - - - - - - - - - - - - def final(self): # is there any residue remaining to be processed? if not self.finalized:# and len(self.buf): self._increment_counter(len(self.buf)) self._set_lastblock() # add padding self.buf += (chr(0).encode())*(self.BLOCKBYTES - len(self.buf)) print(len(self.buf)) print(self.buf) # final compress self._compress(self.buf) self.buf = b'' # nothing more (no residue) # convert 8 LE words into digest (bytestring) self.digest_ = struct.pack('<8%s' % self.WORDFMT, *tuple(self.h)) self.finalized = True return self.digest_[:self.digest_size] digest = final def hexdigest(self): return binascii.hexlify(self.final()).decode() def _set_lastblock(self): if self.last_node: self.f[1] = self.MASKBITS self.f[0] = self.MASKBITS def _increment_counter(self, numbytes): self.totbytes += numbytes self.t[0] = self.totbytes & self.MASKBITS self.t[1] = self.totbytes >> self.WORDBITS # - - - - - - - - - - - - - - - - - - - - - - - - - - - # common utility functions def copy(self): return copy.deepcopy(self) #--------------------------------------------------------------- class BLAKE2b(BLAKE2): WORDBITS = 64 WORDBYTES = 8 MASKBITS = MASK64BITS WORDFMT = 'Q' # used in _compress() and final() ROUNDS = 12 BLOCKBYTES = 128 OUTBYTES = 64 KEYBYTES = 64 SALTBYTES = 16 # see also hardcoded value in ParamFields64 PERSONALBYTES = 16 # see also hardcoded value in ParamFields64 IV = [ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 ] ROT1 = 32 ROT2 = 24 ROT3 = 16 ROT4 = 63 # - - - - - - - - - - - - - - - - - - - - - - - - - - - def __init__(self, data=b'', digest_size=64, key=b'', salt=b'', person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, node_depth=0, inner_size=0, last_node=False): assert 1 <= digest_size <= self.OUTBYTES assert len(key) <= self.KEYBYTES assert len(salt) <= self.SALTBYTES assert len(person) <= self.PERSONALBYTES assert 0 <= fanout <= MASK8BITS assert 0 <= depth <= MASK8BITS assert 0 <= leaf_size <= MASK32BITS assert 0 <= node_offset <= MASK64BITS assert 0 <= node_depth <= MASK8BITS assert 0 <= inner_size <= MASK8BITS # - - - - - - - - - - - - - - - - - - - - - - - - - # use ctypes LittleEndianStructure and Union as a # convenient way to organize complex structs, convert # to little endian, and access by words class ParamFields64(LittleEndianStructure): _fields_ = [("digest_size", c_ubyte), ("key_length", c_ubyte), ("fanout", c_ubyte), ("depth", c_ubyte), ("leaf_size", c_uint32), ("node_offset_lo", c_uint32), ("node_offset_hi", c_uint32), ("node_depth", c_ubyte), ("inner_size", c_ubyte), ("reserved", c_char * 14), ("salt", c_char * 16), ("person", c_char * 16), ] class Params64(Union): _fields_ = [("F", ParamFields64), ("W", c_uint64 * 8), ] # this next makes PARAMS a 'proper' instance variable self.PARAMS = Params64 # key is passed as an argument; all other variables are # defined as instance variables self.digest_size = digest_size self.data = data self.salt = salt self.person = person self.fanout = fanout self.depth = depth self.leaf_size = leaf_size self.node_offset = node_offset self.node_depth = node_depth self.inner_size = inner_size self.last_node = last_node # now call init routine common to BLAKE2b and BLAKE2s self._init(key=key) #--------------------------------------------------------------- class BLAKE2s(BLAKE2): WORDBITS = 32 WORDBYTES = 4 MASKBITS = MASK32BITS WORDFMT = 'L' # used in _compress() and final() ROUNDS = 10 BLOCKBYTES = 64 OUTBYTES = 32 KEYBYTES = 32 SALTBYTES = 8 PERSONALBYTES = 8 IV = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 ] ROT1 = 16 ROT2 = 12 ROT3 = 8 ROT4 = 7 # - - - - - - - - - - - - - - - - - - - - - - - - - - - def __init__(self, data=b'', digest_size=32, key=b'', salt=b'', person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, node_depth=0, inner_size=0, last_node=False): assert 1 <= digest_size <= self.OUTBYTES assert len(key) <= self.KEYBYTES assert len(salt) <= self.SALTBYTES assert len(person) <= self.PERSONALBYTES assert 0 <= fanout <= MASK8BITS assert 0 <= depth <= MASK8BITS assert 0 <= leaf_size <= MASK32BITS assert 0 <= node_offset <= MASK48BITS assert 0 <= node_depth <= MASK8BITS assert 0 <= inner_size <= MASK8BITS class ParamFields32(LittleEndianStructure): pass ParamFields32.SALTBYTES = self.SALTBYTES ParamFields32.PERSONALBYTES = self.PERSONALBYTES ParamFields32._fields_ = [ ("digest_size", c_ubyte), ("key_length", c_ubyte), ("fanout", c_ubyte), ("depth", c_ubyte), ("leaf_size", c_uint32), ("node_offset_lo", c_uint32), ("node_offset_hi", c_uint16), ("node_depth", c_ubyte), ("inner_size", c_ubyte), ("salt", c_char * self.SALTBYTES), ("person", c_char * self.PERSONALBYTES), ] class Params32(Union): _fields_ = [("F", ParamFields32), ("W", c_uint32 * 8), ] # this next makes PARAMS union a 'proper' instance variable self.PARAMS = Params32 # key is passed as an argument; all other variables are # defined as instance variables self.digest_size = digest_size self.data = data self.salt = salt self.person = person self.fanout = fanout self.depth = depth self.leaf_size = leaf_size self.node_offset = node_offset self.node_depth = node_depth self.inner_size = inner_size self.last_node = last_node # now call init routine common to BLAKE2b and BLAKE2s self._init(key=key) # Example usage if __name__ == "__main__": message = b"" # Manual implementation b2 = BLAKE2s(digest_size=32) b2.update(message) digest = b2.final() manual_hash_value = binascii.hexlify(digest).decode() print("my imp:", manual_hash_value) print("bloke2: c1f4134633d0c0708c8db5d9a8fa708d7b33784de22503c1c8e83d57abc2ec20") # "" # Verify that both results are the same assert manual_hash_value == "c1f4134633d0c0708c8db5d9a8fa708d7b33784de22503c1c8e83d57abc2ec20", "The outputs do not match!" message = b"123" # Manual implementation b2 = BLAKE2s(digest_size=32) b2.update(message) digest = b2.final() manual_hash_value = binascii.hexlify(digest).decode() print("my imp:", manual_hash_value) print("bloke2: 6ea2fe2803fb1e09f90005da46b472aa63e193c4406c75f09773c266460e7672") # "123" # Verify that both results are the same assert manual_hash_value == "6ea2fe2803fb1e09f90005da46b472aa63e193c4406c75f09773c266460e7672", "The outputs do not match!" ``` # Challenge 7 - .NET app is compiled with native AOT, ECDH, chacha20 > filename: `fullspeed.exe`, `capture.pcapng` > filetype: `.NET PE64`, `pcap` > estimated time: ? min - 題目敘述 ``` Has this all been far too easy? Where's the math? Where's the science? Where's the, I don't know.... cryptography? Well we don't know about any of that, but here is a little .NET binary to chew on while you discuss career changes with your life coach. ``` - 解題經歷 這題一支 Windows dotnet 執行程式搭配一個封包, 猜是要解出封包的內容, dotnet 程式是 AOT(ahead of time) 編譯而成, 去除了很多函數的 metadata, 沒辦法用 ILSpy/dnSpy 直接看原始碼, 丟進 IDA Pro 裡會是一堆未知功能的 sub function, 後來參考了一篇[做法](https://harfanglab.io/insidethelab/reverse-engineering-ida-pro-aot-net/), 是使用 IDA Pro 的 FLIRT 特徵, 大致流程如下 猜功能 -> 寫dotnet 程式 -> AOT 編譯 -> 建立 FLIRT Signature -> 在題目裡套用 FLIRT Signature, 如此一來 IDA Pro 裡的 pesudo code 就會好看許多, 需要進行逆向的就只剩下十個 function ![image](https://hackmd.io/_uploads/SJ4vJVJfyl.png) 全部看懂之後, 發現這之程式實作了一套 ECDH 流程, client server 互丟橢圓曲線上的點 (`client_secret*G`, `server_secret*G`), secret 大小為 128 bit, 然後以 sha512 hash `client_secret*server_secret*G` X 軸推出 chacha20 的 key 與 nonce, 作後續 command and control 的加解密方式 仔細分析橢圓曲線的參數會發現其 order 是可分解(smooth), 也就是可以運用 Pohlig-Hellman 在有限時間內推出 secret, 但分解結果裡有一個大質數 `35809 * 46027 * 56369 * 57301 * 65063 * 111659 * 113111 * 7072010737074051173701300310820071551428959987622994965153676442076542799542912293`, 導致想解 112 bit 以上的 secret 一樣得花很久的時間, 因為對密碼學的不熟悉, 就卡死在這裡了 後來看完官方的 writeup, 簡單來說就是還是可以把暴力破解的難度降到 2^16^ - [picoCTF_2017 ECC2](https://github.com/hgarrereyn/Th3g3ntl3man-CTF-Writeups/blob/master/2017/picoCTF_2017/problems/cryptography/ECC2/ECC2.md) ## Development Environment - dotnet (8.0.5 - SDK 8.0.300) ![image](https://hackmd.io/_uploads/H1MkneG-1g.png) - BouncyCastle Cryptography - https://github.com/bcgit/bc-csharp/blob/59f5f71f1a1b4b67ee3d25997350f4a00d07490b/README.md > not `BouncyCastle.Crypto` !!! ``` dotnet add package BouncyCastle.Cryptography ``` - TestApp.csproj ``` <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <PublishAot>true</PublishAot> <InvariantGlobalization>true</InvariantGlobalization> </PropertyGroup> <ItemGroup> </ItemGroup> <ItemGroup> <PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" /> </ItemGroup> </Project> ``` ## Network Flow - pcapng ![image](https://hackmd.io/_uploads/B1yNhUT1kx.png) ![image](https://hackmd.io/_uploads/S1MSojgb1g.png) ``` 00000000 0a 6c 55 90 73 da 49 75 4e 9a d9 84 6a 72 95 47 .lU.s.Iu N...jr.G 00000010 45 e4 f2 92 12 13 ec cd a4 b1 42 2e 2f dd 64 6f E....... ..B./.do 00000020 c7 e2 83 89 c7 c2 e5 1a 59 1e 01 47 e2 eb e7 ae ........ Y..G.... 00000030 26 40 22 da f8 c7 67 6a 1b 27 20 91 7b 82 99 9d &@"...gj .' .{... 00000040 42 cd 18 78 d3 1b c5 7b 6d b1 7b 97 05 c7 ff 24 B..x...{ m.{....$ 00000050 04 cb bf 13 cb db 8c 09 66 21 63 40 45 29 39 22 ........ f!c@E)9" 00000000 a0 d2 eb a8 17 e3 8b 03 cd 06 32 27 bd 32 e3 53 ........ ..2'.2.S 00000010 88 08 18 89 3a b0 23 78 d7 db 3c 71 c5 c7 25 c6 ....:.#x ..<q..%. 00000020 bb a0 93 4b 5d 5e 2d 3c a6 fa 89 ff bb 37 4c 31 ...K]^-< .....7L1 00000030 96 a3 5e af 2a 5e 0b 43 00 21 de 36 1a a5 8f 80 ..^.*^.C .!.6.... 00000040 15 98 1f fd 0d 98 24 b5 0a f2 3b 5c cf 16 fa 4e ......$. ..;\...N 00000050 32 34 83 60 2d 07 54 53 4d 2e 7a 8a af 81 74 dc 24.`-.TS M.z...t. 00000060 f2 72 d5 4c 31 86 0f .r.L1.. 00000060 3f bd 43 da 3e e3 25 ?.C.>.% 00000067 86 df d7 ... 00000067 c5 0c ea 1c 4a a0 64 c3 5a 7f 6e 3a b0 25 84 41 ....J.d. Z.n:.%.A 00000077 ac 15 85 c3 62 56 de a8 3c ac 93 00 7a 0c 3a 29 ....bV.. <...z.:) 00000087 86 4f 8e 28 5f fa 79 c8 eb 43 97 6d 5b 58 7f 8f .O.(_.y. .C.m[X.. 00000097 35 e6 99 54 71 16 5..Tq. 0000006A fc b1 d2 cd bb a9 79 c9 89 99 8c ......y. ... 0000009D 61 49 0b aI. 00000075 ce 39 da .9. 000000A0 57 70 11 e0 d7 6e c8 eb 0b 82 59 33 1d ef 13 ee Wp...n.. ..Y3.... 000000B0 6d 86 72 3e ac 9f 04 28 92 4e e7 f8 41 1d 4c 70 m.r>...( .N..A.Lp 000000C0 1b 4d 9e 2b 37 93 f6 11 7d d3 0d ac ba .M.+7... }.... 00000078 2c ae 60 0b 5f 32 ce a1 93 e0 de 63 d7 09 83 8b ,.`._2.. ...c.... 00000088 d6 . 000000CD a7 fd 35 ..5 00000089 ed f0 fc ... 000000D0 80 2b 15 18 6c 7a 1b 1a 47 5d af 94 ae 40 f6 bb .+..lz.. G]...@.. 000000E0 81 af ce dc 4a fb 15 8a 51 28 c2 8c 91 cd 7a 88 ....J... Q(....z. 000000F0 57 d1 2a 66 1a ca ec W.*f... 0000008C ae c8 d2 7a 7c f2 6a 17 27 36 85 ...z|.j. '6. 000000F7 35 a4 4e 5.N 00000097 2f 39 17 /9. 000000FA ed 09 44 7d ed 79 72 19 c9 66 ef 3d d5 70 5a 3c ..D}.yr. .f.=.pZ< 0000010A 32 bd b1 71 0a e3 b8 7f e6 66 69 e0 b4 64 6f c4 2..q.... .fi..do. 0000011A 16 c3 99 c3 a4 fe 1e dc 0a 3e c5 82 7b 84 db 5a ........ .>..{..Z 0000012A 79 b8 16 34 e7 c3 af e5 28 a4 da 15 45 7b 63 78 y..4.... (...E{cx 0000013A 15 37 3d 4e dc ac 21 59 d0 56 .7=N..!Y .V 0000009A f5 98 1f 71 c7 ea 1b 5d 8b 1e 5f 06 fc 83 b1 de ...q...] .._..... 000000AA f3 8c 6f 4e 69 4e 37 06 41 2e ab f5 4e 3b 6f 4d ..oNiN7. A...N;oM 000000BA 19 e8 ef 46 b0 4e 39 9f 2c 8e ce 84 17 fa ...F.N9. ,..... 00000144 40 08 bc @.. 000000C8 54 e4 1e T.. 00000147 f7 01 fe e7 4e 80 e8 df b5 4b 48 7f 9b 2e 3a 27 ....N... .KH...:' 00000157 7f a2 89 cf 6c b8 df 98 6c dd 38 7e 34 2a c9 f5 ....l... l.8~4*.. 00000167 28 6d a1 1c a2 78 40 84 (m...x@. 000000CB 5c a6 8d 13 94 be 2a 4d 3d 4d 7c 82 e5 \.....*M =M|.. 0000016F 31 b6 da c6 2e f1 ad 8d c1 f6 0b 79 26 5e d0 de 1....... ...y&^.. 0000017F aa 31 dd d2 d5 3a a9 fd 93 43 46 38 10 f3 e2 23 .1...:.. .CF8...# 0000018F 24 06 36 6b 48 41 53 33 d4 b8 ac 33 6d 40 86 ef $.6kHAS3 ...3m@.. 0000019F a0 f1 5e 6e 59 ..^nY 000000D8 0d 1e c0 6f 36 ...o6 ``` - send (client -> server) https://cyberchef.org/#recipe=From_Hexdump()XOR(%7B'option':'Hex','string':'1337'%7D,'Standard',false)To_Hexdump(16,false,false,false)&input=MDAwMDAwMDAgIDBhIDZjIDU1IDkwIDczIGRhIDQ5IDc1ICA0ZSA5YSBkOSA4NCA2YSA3MiA5NSA0NyAgIC5sVS5zLkl1IE4uLi5qci5HDQowMDAwMDAxMCAgNDUgZTQgZjIgOTIgMTIgMTMgZWMgY2QgIGE0IGIxIDQyIDJlIDJmIGRkIDY0IDZmICAgRS4uLi4uLi4gLi5CLi8uZG8NCjAwMDAwMDIwICBjNyBlMiA4MyA4OSBjNyBjMiBlNSAxYSAgNTkgMWUgMDEgNDcgZTIgZWIgZTcgYWUgICAuLi4uLi4uLiBZLi5HLi4uLg0KMDAwMDAwMzAgIDI2IDQwIDIyIGRhIGY4IGM3IDY3IDZhICAxYiAyNyAyMCA5MSA3YiA4MiA5OSA5ZCAgICZAIi4uLmdqIC4nIC57Li4uDQowMDAwMDA0MCAgNDIgY2QgMTggNzggZDMgMWIgYzUgN2IgIDZkIGIxIDdiIDk3IDA1IGM3IGZmIDI0ICAgQi4ueC4uLnsgbS57Li4uLiQNCjAwMDAwMDUwICAwNCBjYiBiZiAxMyBjYiBkYiA4YyAwOSAgNjYgMjEgNjMgNDAgNDUgMjkgMzkgMjIgICAuLi4uLi4uLiBmIWNARSk5Igo - recv (server -> client) https://cyberchef.org/#recipe=From_Hexdump()XOR(%7B'option':'Hex','string':'1337'%7D,'Standard',false)To_Hexdump(16,false,false,false)&input=ICAgIDAwMDAwMDAwICBhMCBkMiBlYiBhOCAxNyBlMyA4YiAwMyAgY2QgMDYgMzIgMjcgYmQgMzIgZTMgNTMgICAuLi4uLi4uLiAuLjInLjIuUw0KICAgIDAwMDAwMDEwICA4OCAwOCAxOCA4OSAzYSBiMCAyMyA3OCAgZDcgZGIgM2MgNzEgYzUgYzcgMjUgYzYgICAuLi4uOi4jeCAuLjxxLi4lLg0KICAgIDAwMDAwMDIwICBiYiBhMCA5MyA0YiA1ZCA1ZSAyZCAzYyAgYTYgZmEgODkgZmYgYmIgMzcgNGMgMzEgICAuLi5LXV4tPCAuLi4uLjdMMQ0KICAgIDAwMDAwMDMwICA5NiBhMyA1ZSBhZiAyYSA1ZSAwYiA0MyAgMDAgMjEgZGUgMzYgMWEgYTUgOGYgODAgICAuLl4uKl4uQyAuIS42Li4uLg0KICAgIDAwMDAwMDQwICAxNSA5OCAxZiBmZCAwZCA5OCAyNCBiNSAgMGEgZjIgM2IgNWMgY2YgMTYgZmEgNGUgICAuLi4uLi4kLiAuLjtcLi4uTg0KICAgIDAwMDAwMDUwICAzMiAzNCA4MyA2MCAyZCAwNyA1NCA1MyAgNGQgMmUgN2EgOGEgYWYgODEgNzQgZGMgICAyNC5gLS5UUyBNLnouLi50Lg0KCg ## AOT (ahead of time) Compilation - PE file does not contain any managed metadata. ![image](https://hackmd.io/_uploads/HJsz3v6kJe.png) - https://eersonmez.blogspot.com/2020/02/ilspy-decompiling-net-core-self.html - https://migeel.sk/blog/2023/09/15/reverse-engineering-natively-compiled-dotnet-apps/ - https://github.com/dotnet/runtime/tree/v8.0.5/src/libraries - https://github.com/bcgit/bc-csharp - https://harfanglab.io/insidethelab/reverse-engineering-ida-pro-aot-net/ - https://blog.cyberethical.me/htb-cyber-apocalypse-forensics-oblique-final - BouncyCastle Examples - https://downloads.bouncycastle.org/csharp/docs/BC-CSharpDotNet-Examples.zip ## Tools - FLIRT - https://github.com/ZackMount/NativeAOT-Signatures - [flare-ida idb2pat.py](https://github.com/mandiant/flare-ida/blob/master/python/flare/idb2pat.py) ## Static Analysis - .NET core version - `.NETCoreApp,Version=v8.0` - `8.0.524.21615\8.0.5+087e15321bb712ef6fe8b0ba6f8bd12facf92629` ## Some string obfuscation - sub_7FF7E24E7D80 ![image](https://hackmd.io/_uploads/ByJielx-Je.png) - decrypt.py ```python import binascii def decrypt(enc_str): enc_bytes = binascii.unhexlify(enc_str) j = 0 dec_bytes = b"" for e in enc_bytes: j = 13 * j + 37 dec_bytes += bytes([e ^ (j & 0xFF)]) print(dec_bytes) return dec_bytes enc_addr_info = decrypt("143f41d2c05427964848a505f9698c43e4c5905b") # 192.168.56.103;31337 decrypt("1e") # ; decrypt("4b731f90") # null decrypt("51691cdc9d0d71df") # too long decrypt("59") # | decrypt("4662") # cd decrypt("4a6d") # ok decrypt("4975") # ls decrypt("183b4edc950b6dcb5d43b609") # === dirs === decrypt("0b") # . decrypt("183b4edc970b73dd0e5eb609f4") # === files === decrypt("466707") # cat decrypt("407e1a88") # exit decrypt("476717dc920f7b") # bad cmd decrypt("407401") # err decrypt("53630195971b3fde1c17e751ad") # verify failed decrypt("53630195971b") # verify decrypt("4c6815") # inf buf_1337 = decrypt("143540cbc0512c8f4c4db803f8698447e4c5905b90617c1f1c5d88934879d4d7b4d5e0eb60714cafec6dd8231809246704e5307b30019c3fbc7d28b3e81974f7d4f5008b8011ec4f0c0d78c3b8294407a485501b50213cdfdc1d485308399497") # 133713371337133713371337133713371337133713371337133713371337133713371337133713371337133713371337 buf_1 = decrypt("463f43cdc15079d91c4ab352f862d545b097c05dc765794a4f5a8bc54828de86e7d6b7e4657348a9ef3c89711a5f226502e0627b60079931ba7878b6bb4b22a384f20484811be84e080c73c7e87b4707ad835b1f04236aded81f4c565839c1c4") # c90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd buf_2 = decrypt("443644c595002f80181fb900fe6a8445e59592549366771f4f5b8bc24e7dd7d7e182e7ea307747f9ec3ada22195c71670ce43a7b6551cc31ef287ae0ef4921a3dcf052848041eb1904097ec2bd2b4608f482531f5223698ddd4818550a3890c6") # a079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f buf_3 = decrypt("1c604acfc8012f8a1c49e950fe3cd442e3c5c258c2312a1c1c58dd901a7fd0d5e3d4ebb861214eabec6b88204f0a74620de4652f60049838b42f2ee2e04c70a6dca501898041e61d585a2ecdec784652f4d7501d57223dd9db191d050c399f90") # 9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380 buf_4 = decrypt("153e449ec4047a8b1c1bbd50aa3cd540b0c69458c3667f4e1b5c8b9c1a7281d6e183e7ba65244faeea678f22100924670ce0677f630bcd6cbb7b22b3e91e21a2ddf307898344ef4c0c582d92b82e1456a5d35a4d00256adcd81b4f505f33c398") # 087b5fe3ae6dcfb0e074b40f6208c8f6de4f4f0679d6933796d3b9bd659704fb85452f041fff14cf0e9aa7e45544f9d8 buf_5 = decrypt("143444c8c3577c89194db804ac3e8243e2c0955fc46a781c1857dec5187b85d1e7d3e0b935241aabed6b8d22480d2e3204ee372e32039738bd7d28e5b84876f9d5a35185d043ef480e5b7bc6ec231352f380071958216cdd881d1954013b9f92") # 127425c1d330ed537663e87459eaa1b1b53edfe305f6a79b184b3180033aab190eb9aa003e02e9dbf6d593c5e3b08182 ``` ## Chacha20 (key 32 bytes, nonce 8 bytes) - key of chacha20 is derived from sha512_hash result - 7FF7E24E819D call sha512_hash - 7FF7E24E8461 call chacha20 - sample ``` b4 sha512 hash src of buf_0x30 (have value after .managed:00007FF7E24E813F call qword ptr [rax+88h]) (.managed:00007FF7E2456B87 call qword ptr [rax+50h] ; sub_7FF7E2455F10) 0000025176D390D0 60 BB 56 E2 F7 7F 00 00 0C 00 00 00 00 00 00 00 `»Vâ÷........... 0000025176D390E0 F7 2F 71 7F 45 83 2E FB 49 D4 9D A1 BE 6B 59 87 ÷/q.Eƒ.ûIÔ.¡¾kY‡ 0000025176D390F0 FF FC C6 2E D8 88 A8 57 CA CD 35 84 DE E9 19 6A ÿüÆ.؈¨WÊÍ5„Þé.j 0000025176D39100 67 19 01 E2 1E 7B 96 45 E8 B5 38 D9 D4 CE FC 24 g..â.{–Eèµ8ÙÔÎü$ b4 sha512 hash buf_0x30 0000025174AD91D0 88 B6 56 E2 F7 7F 00 00 30 00 00 00 00 00 00 00 ˆ¶Vâ÷...0....... 0000025174AD91E0 7F 71 2F F7 FB 2E 83 45 A1 9D D4 49 87 59 6B BE .q/÷û.ƒE¡.ÔI‡Yk¾ 0000025174AD91F0 2E C6 FC FF 57 A8 88 D8 84 35 CD CA 6A 19 E9 DE .ÆüÿW¨ˆØ„5ÍÊj.éÞ 0000025174AD9200 E2 01 19 67 45 96 7B 1E D9 38 B5 E8 24 FC CE D4 â..gE–{.Ù8µè$üÎÔ sha512 result 1 0000025176D39760 00 00 00 00 00 00 00 00 88 B6 56 E2 F7 7F 00 00 ........ˆ¶Vâ÷... 0000025176D39770 40 00 00 00 00 00 00 00 2D 8E 5D E2 A2 9B 02 57 @.......-Ž]⢛.W 0000025176D39780 C0 73 72 57 61 23 61 4F 30 85 E7 34 C6 67 CC 99 ÀsrWa#aO0…ç4ÆgÌ™ 0000025176D39790 E5 01 C7 CF 38 CA 48 3F AE 1E A1 5D C9 1C 70 E7 å.ÇÏ8ÊH?®.¡]É.pç 0000025176D397A0 5E 86 1E 77 B3 24 F8 CD 46 65 E2 BC 1F B5 0A B7 ^†.w³$øÍFeâ¼.µ.· 0000025176D397B0 2E 95 C3 C2 FC 9B 3A 95 00 00 00 00 00 00 00 00 .•ÃÂü›:•........ chacha20 initial state (c2 - client) 0000025176D39FF0 60 BB 56 E2 F7 7F 00 00 10 00 00 00 00 00 00 00 `»Vâ÷........... 0000025176D3A000 65 78 70 61 6E 64 20 33 32 2D 62 79 74 65 20 6B expand 32-byte k 0000025176D3A010 2D 8E 5D E2 A2 9B 02 57 C0 73 72 57 61 23 61 4F -Ž]⢛.WÀsrWa#aO 0000025176D3A020 30 85 E7 34 C6 67 CC 99 E5 01 C7 CF 38 CA 48 3F 0…ç4ÆgÌ™å.ÇÏ8ÊH? 0000025176D3A030 00 00 00 00 00 00 00 00 AE 1E A1 5D C9 1C 70 E7 ........®.¡]É.pç data c2 - client (encrypted "verify") \x7c\xbb\xd4\x66\x75\xc9\x23 data client - c2 (encrypted "verify") 0000025176D3A700 64 73 18 8C 2D 43 08 00 00 00 00 00 00 00 00 00 ds.Œ-C.......... chacha20 initial state (client - c2) 0000025176D39FF0 60 BB 56 E2 F7 7F 00 00 10 00 00 00 00 00 00 00 `»Vâ÷........... 0000025176D3A000 65 78 70 61 6E 64 20 33 32 2D 62 79 74 65 20 6B expand 32-byte k 0000025176D3A010 2D 8E 5D E2 A2 9B 02 57 C0 73 72 57 61 23 61 4F -Ž]⢛.WÀsrWa#aO 0000025176D3A020 30 85 E7 34 C6 67 CC 99 E5 01 C7 CF 38 CA 48 3F 0…ç4ÆgÌ™å.ÇÏ8ÊH? 0000025176D3A030 01 00 00 00 00 00 00 00 AE 1E A1 5D C9 1C 70 E7 ........®.¡]É.pç ``` https://cyberchef.org/#recipe=ChaCha(%7B'option':'Hex','string':'2D%208E%205D%20E2%20A2%209B%2002%2057%20C0%2073%2072%2057%2061%2023%2061%204F%20%2030%2085%20E7%2034%20C6%2067%20CC%2099%20E5%2001%20C7%20CF%2038%20CA%2048%203F'%7D,%7B'option':'Hex','string':'AE%201E%20A1%205D%20C9%201C%2070%20E7'%7D,0,'20','Hex','Hex')From_Hex('Auto')To_Hexdump(16,false,false,false)&input=eDdjXHhiYlx4ZDRceDY2XHg3NVx4YzlceDIz ## Implementation of Chacha20 (key 32 bytes, nonce 8 bytes) ```python import struct # Rotate left (32-bit) def rotl32(x, n): return ((x << n) & 0xffffffff) | (x >> (32 - n)) # ChaCha20 quarter-round function def quarterround(state, a, b, c, d): state[a] = (state[a] + state[b]) & 0xffffffff state[d] ^= state[a] state[d] = rotl32(state[d], 16) state[c] = (state[c] + state[d]) & 0xffffffff state[b] ^= state[c] state[b] = rotl32(state[b], 12) state[a] = (state[a] + state[b]) & 0xffffffff state[d] ^= state[a] state[d] = rotl32(state[d], 8) state[c] = (state[c] + state[d]) & 0xffffffff state[b] ^= state[c] state[b] = rotl32(state[b], 7) # ChaCha20 block function for an 8-byte nonce def chacha20_block(key, counter, nonce): # ChaCha20 constants ("expand 32-byte k" in ASCII, split into 32-bit words) constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574] # ChaCha20 constants ("expand 16-byte k" in ASCII, split into 32-bit words) #constants = [0x61707865, 0x3120646e, 0x79622d36, 0x6b206574] # Convert key to 32-bit words key_words = struct.unpack('<8I', key) # Convert 64-bit counter and 64-bit nonce to 32-bit words counter_words = struct.unpack('<2I', struct.pack('<Q', counter)) nonce_words = struct.unpack('<2I', nonce) # Initialize the state array (16 32-bit integers) state = [ constants[0], constants[1], constants[2], constants[3], key_words[0], key_words[1], key_words[2], key_words[3], key_words[4], key_words[5], key_words[6], key_words[7], counter_words[0], counter_words[1], nonce_words[0], nonce_words[1] ] # Run 20 rounds (10 double rounds) working_state = state[:] for _ in range(10): # Column round quarterround(working_state, 0, 4, 8, 12) quarterround(working_state, 1, 5, 9, 13) quarterround(working_state, 2, 6, 10, 14) quarterround(working_state, 3, 7, 11, 15) # Diagonal round quarterround(working_state, 0, 5, 10, 15) quarterround(working_state, 1, 6, 11, 12) quarterround(working_state, 2, 7, 8, 13) quarterround(working_state, 3, 4, 9, 14) # Add the original state to the result and return output = [(working_state[i] + state[i]) & 0xffffffff for i in range(16)] return b''.join(struct.pack('<I', word) for word in output) # ChaCha20 encryption function: XORs the keystream with plaintext def chacha20_encrypt(key, nonce, plaintext): if len(key) != 32 or len(nonce) != 8: raise ValueError("Key must be 32 bytes and nonce 8 bytes") ciphertext = bytearray(len(plaintext)) block_counter = 0 for i in range(0, len(plaintext), 64): keystream = chacha20_block(key, block_counter, nonce) block = plaintext[i:i+64] keystream_block = keystream[:len(block)] # XOR plaintext block with keystream to produce ciphertext block for j in range(len(block)): ciphertext[i + j] = block[j] ^ keystream_block[j] block_counter += 1 return bytes(ciphertext) # Example usage if __name__ == "__main__": key = b"\x2D\x8E\x5D\xE2\xA2\x9B\x02\x57\xC0\x73\x72\x57\x61\x23\x61\x4F\x30\x85\xE7\x34\xC6\x67\xCC\x99\xE5\x01\xC7\xCF\x38\xCA\x48\x3F" # 32 bytes nonce = b"\xAE\x1E\xA1\x5D\xC9\x1C\x70\xE7" # 8 bytes plaintext = b"verify\x00" # plaintext # Encrypt ciphertext = chacha20_encrypt(key, nonce, plaintext) print("Ciphertext:", ciphertext) # Decrypt (Salsa20 is symmetric, so encrypting again decrypts) decrypted = chacha20_encrypt(key, nonce, ciphertext) print("Decrypted:", decrypted) ``` ## ECDH > ref: sub_7FF7BA3F7BC0 ![image](https://hackmd.io/_uploads/HJoG7xxZyx.png) ```csharp using System; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Math.EC.Custom.Sec; using Org.BouncyCastle.Security; namespace ECCValidation { class Program { static void Main(string[] args) { // Split the data into x and y coordinates (each 48 bytes for P-384) byte[] xBytes = new byte[48]; byte[] yBytes = new byte[48]; Array.Copy(pointData, 0, xBytes, 0, 48); Array.Copy(pointData, 48, yBytes, 0, 48); // Convert x and y to BigInteger BigInteger x_recv = new BigInteger(1, xBytes); // '1' specifies a positive number BigInteger y_recv = new BigInteger(1, yBytes); // Load P-384 curve parameters //X9ECParameters ecParams = ECNamedCurveTable.GetByName("P-384"); //var curve = ecParams.Curve; // Custom curve parameters (ref: sub_7FF7BA3F7BC0) // Define the custom prime modulus p BigInteger p = new BigInteger("c90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd", 16); // Define coefficients a and b (example values here, replace with actual values for your curve) BigInteger a = new BigInteger("a079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f", 16); BigInteger b = new BigInteger("9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380", 16); BigInteger order = BigInteger.Zero; BigInteger cofactor = BigInteger.Zero; // Create the custom elliptic curve ECCurve curve = new FpCurve(p, a, b); // Generator point BigInteger x_G = new BigInteger("087b5fe3ae6dcfb0e074b40f6208c8f6de4f4f0679d6933796d3b9bd659704fb85452f041fff14cf0e9aa7e45544f9d8", 16); BigInteger y_G = new BigInteger("127425c1d330ed537663e87459eaa1b1b53edfe305f6a79b184b3180033aab190eb9aa003e02e9dbf6d593c5e3b08182", 16); ECPoint generator = curve.CreatePoint(x_G, y_G); //SecureRandom random = SecureRandom.GetInstance("SHA256PRNG"); //BigInteger randomScalar = new BigInteger(384, random); BigInteger rand = new BigInteger("96f6e8b7f27c4850e4c72e251926aece", 16); ECPoint randomPoint = generator.Multiply(rand); ECPoint normalizedPoint = randomPoint.Normalize(); Console.WriteLine("X: " + normalizedPoint.AffineXCoord.ToBigInteger().ToString(16)); Console.WriteLine("Y: " + normalizedPoint.AffineYCoord.ToBigInteger().ToString(16)); // Byte stream representing an EC point (x and y coordinates) byte[] pointData = new byte[] { 0xb3,0xe5,0xf8,0x9f,0x04,0xd4,0x98,0x34,0xde,0x31,0x21,0x10,0xae,0x05,0xf0,0x64,0x9b,0x3f,0x0b,0xbe,0x29,0x87,0x30,0x4f,0xc4,0xec,0x2f,0x46,0xd6,0xf0,0x36,0xf1,0xa8,0x97,0x80,0x7c,0x4e,0x69,0x3e,0x0b,0xb5,0xcd,0x9a,0xc8,0xa8,0x00,0x5f,0x06,0x85,0x94,0x4d,0x98,0x39,0x69,0x18,0x74,0x13,0x16,0xcd,0x01,0x09,0x92,0x9c,0xb7,0x06,0xaf,0x0c,0xca,0x1e,0xaf,0x37,0x82,0x19,0xc5,0x28,0x6b,0xdc,0x21,0xe9,0x79,0x21,0x03,0x90,0x57,0x3e,0x30,0x47,0x64,0x5e,0x19,0x69,0xbd,0xbc,0xb6,0x67,0xeb }; // Attempt to create the point on the curve try { ECPoint point2 = curve.ValidatePoint(x_recv, y_recv); Console.WriteLine("Point is valid on the curve:"); Console.WriteLine("X: " + point2.AffineXCoord.ToBigInteger().ToString(16)); Console.WriteLine("Y: " + point2.AffineYCoord.ToBigInteger().ToString(16)); ECPoint randomPoint2 = point2.Multiply(rand); ECPoint normalizedPoint2 = randomPoint2.Normalize(); Console.WriteLine("data for SHA512"); Console.WriteLine("X: " + normalizedPoint2.AffineXCoord.ToBigInteger().ToString(16)); //Console.WriteLine("Y: " + normalizedPoint2.AffineYCoord.ToBigInteger().ToString(16)); } catch (Exception e) { Console.WriteLine("The point is not valid on the curve: " + e.Message); } } } } ``` ## Random number generator > sub_7FF7E24E7E20 ![image](https://hackmd.io/_uploads/HJxqT4glZyl.png) ## ECC ~~Rabbit Hole~~ > ref: https://lazzzaro.github.io/2020/11/07/crypto-ECC/#Pohlig-Hellman%E7%AE%97%E6%B3%95 - p: `0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd` - a: `0xa079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f` - b: `0x9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380` - order: `0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e547761ec3ea549979d50c95478998110005c8c2b7f3498ee71` `Order of the curve: 35809 * 46027 * 56369 * 57301 * 65063 * 111659 * 113111 * 7072010737074051173701300310820071551428959987622994965153676442076542799542912293` - poc.sage ``` import time p = 0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd a = 0xa079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f b = 0x9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380 F = GF(p) E = EllipticCurve(F, [a, b]) N = E.order() print("Order of the curve:", N.factor()) x_G = F(0x087b5fe3ae6dcfb0e074b40f6208c8f6de4f4f0679d6933796d3b9bd659704fb85452f041fff14cf0e9aa7e45544f9d8) y_G = F(0x127425c1d330ed537663e87459eaa1b1b53edfe305f6a79b184b3180033aab190eb9aa003e02e9dbf6d593c5e3b08182) G = E(x_G, y_G) order_G = G.order() print("Order of the G:", hex(order_G)) x_hex1 = "195b46a760ed5a425dadcab37945867056d3e1a50124fffab78651193cea7758d4d590bed4f5f62d4a291270f1dcf499" y_hex1 = "357731edebf0745d081033a668b58aaa51fa0b4fc02cd64c7e8668a016f0ec1317fcac24d8ec9f3e75167077561e2a15" x_hex2 = "b3e5f89f04d49834de312110ae05f0649b3f0bbe2987304fc4ec2f46d6f036f1a897807c4e693e0bb5cd9ac8a8005f06" y_hex2 = "85944d98396918741316cd0109929cb706af0cca1eaf378219c5286bdc21e979210390573e3047645e1969bdbcb667eb" x_hex3 = "b65c41823709a297d2f884d573f86613b215a76131b30736a1fca665fbc5720e721b077a10717a81a0d43ce6a8a5d93f" y_hex3 = "2c1b0fe9b00467aa9f4670f30ec1de25b8695d3cee8ee212db8003a58b504d936c44d03da3b17e97a8edf9db25a0e7b1" x_c = F(int(x_hex1, 16)) y_c = F(int(y_hex1, 16)) x_s = F(int(x_hex2, 16)) y_s = F(int(y_hex2, 16)) x_test = F(int(x_hex3, 16)) y_test = F(int(y_hex3, 16)) try: QTest = E(x_test, y_test) print(f"The point ({x_test}, {y_test}) is on the curve.") except ValueError: print(f"The point ({x_test}, {y_test}) is NOT on the curve.") try: QA = E(x_c, y_c) print(f"The point ({x_c}, {y_c}) is on the curve.") except ValueError: print(f"The point ({x_c}, {y_c}) is NOT on the curve.") try: QB = E(x_s, y_s) print(f"The point ({x_s}, {y_s}) is on the curve.") except ValueError: print(f"The point ({x_s}, {y_s}) is NOT on the curve.") # Factorize the order of P factorization = N.factor() factors, exponents = zip(*factor(N)) primes = [factors[i] ^ exponents[i] for i in range(len(factors))] print(primes) dlogs = [] for fac in primes: t = int(int(N) // int(fac)) print("t: " + str(t)) start_time = time.time() dlog = discrete_log(t*QA, t*G, operation="+") end_time = time.time() time_cost = end_time - start_time dlogs += [dlog] print("factor: " + str(fac) + ", Discrete Log: " + str(dlog) + ", time: " + str(time_cost)) #calculates discrete logarithm for each prime order l = crt(dlogs,primes) print("l: ", l) lg = l*G x, y = lg.xy() print(f"The point ({x_test}, {y_test})") print(f"The point ({x}, {y})") ``` - solve.sage ``` import time p = 0xc90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd a = 0xa079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f b = 0x9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380 F = GF(p) E = EllipticCurve(F, [a, b]) N = E.order() print("Order of the curve:", N.factor()) x_G = F(0x087b5fe3ae6dcfb0e074b40f6208c8f6de4f4f0679d6933796d3b9bd659704fb85452f041fff14cf0e9aa7e45544f9d8) y_G = F(0x127425c1d330ed537663e87459eaa1b1b53edfe305f6a79b184b3180033aab190eb9aa003e02e9dbf6d593c5e3b08182) G = E(x_G, y_G) order_G = G.order() print("Order of the G:", hex(order_G)) x_hex_client = "195b46a760ed5a425dadcab37945867056d3e1a50124fffab78651193cea7758d4d590bed4f5f62d4a291270f1dcf499" y_hex_client = "357731edebf0745d081033a668b58aaa51fa0b4fc02cd64c7e8668a016f0ec1317fcac24d8ec9f3e75167077561e2a15" x_hex_server = "b3e5f89f04d49834de312110ae05f0649b3f0bbe2987304fc4ec2f46d6f036f1a897807c4e693e0bb5cd9ac8a8005f06" y_hex_server = "85944d98396918741316cd0109929cb706af0cca1eaf378219c5286bdc21e979210390573e3047645e1969bdbcb667eb" x_hex3 = "b65c41823709a297d2f884d573f86613b215a76131b30736a1fca665fbc5720e721b077a10717a81a0d43ce6a8a5d93f" y_hex3 = "2c1b0fe9b00467aa9f4670f30ec1de25b8695d3cee8ee212db8003a58b504d936c44d03da3b17e97a8edf9db25a0e7b1" x_client = F(int(x_hex_client, 16)) y_client = F(int(y_hex_client, 16)) x_server = F(int(x_hex_server, 16)) y_server = F(int(y_hex_server, 16)) x_test = F(int(x_hex3, 16)) y_test = F(int(y_hex3, 16)) QTest = E(x_test, y_test) QA = E(x_client, y_client) QB = E(x_server, y_server) # Factorize the order of P factorization = N.factor() factors, exponents = zip(*factor(N)) primes = [factors[i] ^ exponents[i] for i in range(len(factors))] print(primes) factors = factor(order_G, proof=False) smooth_part = Factorization(factors[:-1]).value() big_part = Factorization(factors[-1:]).value() print(smooth_part) print(big_part) secret_mod_smooth = (G*big_part).discrete_log(QA*big_part) print(secret_mod_smooth) possible_secret = secret_mod_smooth while True: if QA == possible_secret * G: break possible_secret += smooth_part print(possible_secret) # 168606034648973740214207039875253762473 ``` ## All in One (for FLIRT) ```csharp using System; using System.Net.Sockets; using System.Text; using System.Threading; using System.Security.Cryptography; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Math.EC.Custom.Sec; using Org.BouncyCastle.Security; namespace ECCValidation { class Program { static void Main(string[] args) { // Create ChaCha20 engine and initialize with key and nonce byte[] key = new byte[] { 0x2D, 0x8E, 0x5D, 0xE2, 0xA2, 0x9B, 0x02, 0x57, 0xC0, 0x73, 0x72, 0x57, 0x61, 0x23, 0x61, 0x4F, 0x30, 0x85, 0xE7, 0x34, 0xC6, 0x67, 0xCC, 0x99, 0xE5, 0x01, 0xC7, 0xCF, 0x38, 0xCA, 0x48, 0x3F }; byte[] nonce = new byte[] { 0xAE, 0x1E, 0xA1, 0x5D, 0xC9, 0x1C, 0x70, 0xE7 }; var engine = new ChaChaEngine(20); var parameters = new ParametersWithIV(new KeyParameter(key), nonce); engine.Init(true, parameters); string plaintext = "verify"; byte[] data = Encoding.UTF8.GetBytes(plaintext); // Allocate buffer for output byte[] output = new byte[data.Length]; // Encrypt each byte individually using ReturnByte for (int i = 0; i < data.Length; i++) { output[i] = engine.ReturnByte(data[i]); } Console.WriteLine("Ciphertext: " + BitConverter.ToString(output).Replace("-", " ")); // Custom curve parameters (ref: sub_7FF7BA3F7BC0) // Define the custom prime modulus p BigInteger p = new BigInteger("c90102faa48f18b5eac1f76bb40a1b9fb0d841712bbe3e5576a7a56976c2baeca47809765283aa078583e1e65172a3fd", 16); //bool isPrime = p.IsProbablePrime(20); //Console.WriteLine(isPrime); // Define coefficients a and b (example values here, replace with actual values for your curve) BigInteger a = new BigInteger("a079db08ea2470350c182487b50f7707dd46a58a1d160ff79297dcc9bfad6cfc96a81c4a97564118a40331fe0fc1327f", 16); BigInteger b = new BigInteger("9f939c02a7bd7fc263a4cce416f4c575f28d0c1315c4f0c282fca6709a5f9f7f9c251c9eede9eb1baa31602167fa5380", 16); //BigInteger order = BigInteger.Zero; //BigInteger cofactor = BigInteger.Zero; // Create the custom elliptic curve Org.BouncyCastle.Math.EC.ECCurve curve = new FpCurve(p, a, b); // order, cofactor); // Identity point Org.BouncyCastle.Math.EC.ECPoint identity = curve.Infinity; // Generator point BigInteger x_G = new BigInteger("087b5fe3ae6dcfb0e074b40f6208c8f6de4f4f0679d6933796d3b9bd659704fb85452f041fff14cf0e9aa7e45544f9d8", 16); BigInteger y_G = new BigInteger("127425c1d330ed537663e87459eaa1b1b53edfe305f6a79b184b3180033aab190eb9aa003e02e9dbf6d593c5e3b08182", 16); Org.BouncyCastle.Math.EC.ECPoint generator = curve.CreatePoint(x_G, y_G); Console.WriteLine("X: " + generator.AffineXCoord.ToBigInteger().ToString(16)); Console.WriteLine("Y: " + generator.AffineYCoord.ToBigInteger().ToString(16)); /* Definition of identity Org.BouncyCastle.Math.EC.ECPoint result = generator.Add(identity); bool isIdentityPreserved = result.Equals(generator); Console.WriteLine("Is identity preserved (P + O = P)? " + isIdentityPreserved); // Get Order Org.BouncyCastle.Math.EC.ECPoint tmp = generator.Add(generator); int i = 1; while (tmp != identity) { tmp = tmp.Add(generator); Console.WriteLine("X: " + tmp.Normalize().AffineXCoord.ToBigInteger().ToString(16)); Console.WriteLine("Y: " + tmp.Normalize().AffineYCoord.ToBigInteger().ToString(16)); Console.WriteLine(i); i += 1; } Console.WriteLine(i); */ SecureRandom secureRandom = new SecureRandom(); int bitLength = 128; BigInteger randomScalar = new BigInteger(bitLength, secureRandom); //BigInteger randomScalar = new BigInteger("96f6e8b7f27c4850e4c72e251926aece", 16); Console.WriteLine("Random BigInteger: " + randomScalar.ToString(16)); Org.BouncyCastle.Math.EC.ECPoint randomPoint = generator.Multiply(randomScalar); Org.BouncyCastle.Math.EC.ECPoint publicKey = randomPoint.Normalize(); Console.WriteLine("X: " + publicKey.AffineXCoord.ToBigInteger().ToString(16)); Console.WriteLine("Y: " + publicKey.AffineYCoord.ToBigInteger().ToString(16)); // Byte stream representing an EC point (x and y coordinates) byte[] pointData2 = new byte[] { 0xb3,0xe5,0xf8,0x9f,0x04,0xd4,0x98,0x34,0xde,0x31,0x21,0x10,0xae,0x05,0xf0,0x64,0x9b,0x3f,0x0b,0xbe,0x29,0x87,0x30,0x4f,0xc4,0xec,0x2f,0x46,0xd6,0xf0,0x36,0xf1,0xa8,0x97,0x80,0x7c,0x4e,0x69,0x3e,0x0b,0xb5,0xcd,0x9a,0xc8,0xa8,0x00,0x5f,0x06,0x85,0x94,0x4d,0x98,0x39,0x69,0x18,0x74,0x13,0x16,0xcd,0x01,0x09,0x92,0x9c,0xb7,0x06,0xaf,0x0c,0xca,0x1e,0xaf,0x37,0x82,0x19,0xc5,0x28,0x6b,0xdc,0x21,0xe9,0x79,0x21,0x03,0x90,0x57,0x3e,0x30,0x47,0x64,0x5e,0x19,0x69,0xbd,0xbc,0xb6,0x67,0xeb }; // Split the data into x and y coordinates (each 48 bytes for P-384) byte[] xBytes = new byte[48]; byte[] yBytes = new byte[48]; Array.Copy(pointData2, 0, xBytes, 0, 48); Array.Copy(pointData2, 48, yBytes, 0, 48); // Convert x and y to BigInteger BigInteger x_recv = new BigInteger(1, xBytes); // '1' specifies a positive number BigInteger y_recv = new BigInteger(1, yBytes); // Attempt to create the point on the curve try { Org.BouncyCastle.Math.EC.ECPoint point2 = curve.ValidatePoint(x_recv, y_recv); Console.WriteLine("Point is valid on the curve:"); Console.WriteLine("X: " + point2.AffineXCoord.ToBigInteger().ToString(16)); Console.WriteLine("Y: " + point2.AffineYCoord.ToBigInteger().ToString(16)); Org.BouncyCastle.Math.EC.ECPoint randomPoint2 = point2.Multiply(randomScalar); Org.BouncyCastle.Math.EC.ECPoint normalizedPoint2 = randomPoint2.Normalize(); Console.WriteLine("X: " + normalizedPoint2.AffineXCoord.ToBigInteger().ToString(16)); Console.WriteLine("Y: " + normalizedPoint2.AffineYCoord.ToBigInteger().ToString(16)); BigInteger xCoord = normalizedPoint2.AffineXCoord.ToBigInteger(); byte[] xCoordBytes = xCoord.ToByteArray(); //byte[] inputBytes = Encoding.UTF8.GetBytes("Hello world"); byte[] hashBytes = SHA512.HashData(xCoordBytes); StringBuilder hash = new StringBuilder(); foreach (byte bb in hashBytes) { hash.Append(bb.ToString("x2")); } Console.WriteLine($"SHA-512 Hash of session key X Coord: {hash}"); } catch (Exception e) { Console.WriteLine("The point is not valid on the curve: " + e.Message); } try { // Connect to the server at localhost:5000 TcpClient client = new TcpClient("192.168.1.6", 31337); Console.WriteLine("Connected to server"); // Get the network stream for communication NetworkStream stream = client.GetStream(); // Send a response to the server byte[] xCoord = publicKey.AffineXCoord.ToBigInteger().ToByteArray(); byte[] yCoord = publicKey.AffineYCoord.ToBigInteger().ToByteArray(); byte[] publicKeyByteX = new byte[48]; byte[] publicKeyByteY = new byte[48]; Buffer.BlockCopy(xCoord, xCoord.Length - 48, publicKeyByteX, 0, 48); Buffer.BlockCopy(yCoord, yCoord.Length - 48, publicKeyByteY, 0, 48); stream.Write(publicKeyByteX); Console.WriteLine("Sent X"); Thread.Sleep(1000); stream.Write(publicKeyByteY); Console.WriteLine("Sent Y"); // Receive message from the server byte[] buffer = new byte[1024]; int bytesRead = stream.Read(buffer); // Blocking read string receivedMessage = Encoding.UTF8.GetString(buffer, 0, bytesRead); Console.WriteLine("Received from server: " + receivedMessage); // Close the stream and the client connection stream.Close(); client.Close(); } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); } } } } ``` ## Conclusion - ECDH using a 384 bit curve - derive session key using SHA512 ``` P_session_key = client_rand * P_recv hash_digest = sha512(P_session_key.x_coord) chacha_key = hash_digest[:0x20] chacha_nonce = hash_digest[0x20:0x28] ``` - communicate using Chacha20 # Challenge 8 - web3 # Challenge 9 # Challenge 10