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)

- 每次在做 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`

- 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

- 題目敘述
```
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

- 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);
}
}
```

- boy_friend0.jpg

- 結果
- 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);
```

- 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
```

- 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

- 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


```
$ 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)

- 000C33F1 (55B46C79C3F1)

- different library hash

- 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

```
#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

- 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

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

- clue 2

- clue 3 XOR operation


### Others
- Background
- README.md

- 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

全部看懂之後, 發現這之程式實作了一套 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)

- 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


```
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.

- 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

- 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

```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

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