Flare-On 11 Notes (2024)

Official Posts

心得

  • 6/10 - 595 th (8 th in Taiwan)
    Image Not Showing Possible Reasons
    • The image was uploaded to a note which you don't have access to
    • The note which the image was originally uploaded to has been deleted
    Learn More →
  • 每次在做 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

    ​​​​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_amain_b

  • 逆向工程

  • reference

Challenge 3 - aray: yara

filename: aray.yara
filetype: txt
estimated time: 1 hour

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

  • 題目敘述

    ​​​​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 Not Showing Possible Reasons
      • The image was uploaded to a note which you don't have access to
      • The note which the image was originally uploaded to has been deleted
      Learn More →

    • 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
      
      ​​​​​​​​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

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

    • boy_friend0.jpg
      image

  • 結果

    • 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

  • tool

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

  • 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

  • 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

      image

      ​​​​​​​​$ 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
      • 000C33F1 (55B46C79C3F1)
        image
    • different library hash
      image

    • 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

  • 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

    ​​​​#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}
    
  • 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

  • key point
    image

  • solve.py

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

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

    • clue 2
      image

    • clue 3 XOR operation
      image

    image

Others

  • Background

    • README.md
      image

    • 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

mybloke2x implementation (TODO)

  • TODO: make bloke2("123") work
# 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, 後來參考了一篇做法, 是使用 IDA Pro 的 FLIRT 特徵, 大致流程如下 猜功能 -> 寫dotnet 程式 -> AOT 編譯 -> 建立 FLIRT Signature -> 在題目裡套用 FLIRT Signature, 如此一來 IDA Pro 裡的 pesudo code 就會好看許多, 需要進行逆向的就只剩下十個 function
    image
    全部看懂之後, 發現這之程式實作了一套 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, 簡單來說就是還是可以把暴力破解的難度降到 216 - picoCTF_2017 ECC2

Development Environment

  • dotnet (8.0.5 - SDK 8.0.300)
    image

  • 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

    image

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

AOT (ahead of time) Compilation

Tools

Static Analysis

  • .NET core version
    • .NETCoreApp,Version=v8.0
    • 8.0.524.21615\8.0.5+087e15321bb712ef6fe8b0ba6f8bd12facf92629

Some string obfuscation

  • sub_7FF7E24E7D80
    image

  • decrypt.py

    ​​​​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({'option':'Hex','string':'2D 8E 5D E2 A2 9B 02 57 C0 73 72 57 61 23 61 4F 30 85 E7 34 C6 67 CC 99 E5 01 C7 CF 38 CA 48 3F'},{'option':'Hex','string':'AE 1E A1 5D C9 1C 70 E7'},0,'20','Hex','Hex')From_Hex('Auto')To_Hexdump(16,false,false,false)&input=eDdjXHhiYlx4ZDRceDY2XHg3NVx4YzlceDIz

Implementation of Chacha20 (key 32 bytes, nonce 8 bytes)

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

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

ECC Rabbit Hole

ref: https://lazzzaro.github.io/2020/11/07/crypto-ECC/#Pohlig-Hellman算法

  • 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)

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