BUUCTF-Web-wp(1-4)
本文最后更新于 8 天前,其中的信息可能已经有所发展或是发生改变。

PS:以下题目均在BUUCTF

1、极客大挑战 2019 EasySQL(万能账号密码,SQL注入,HackBar)

题目

做法

启动靶机,打开给出的网址

随便输点东西进去,测试一下

输入1、1’、1″判断SQL语句闭合方式

输入以上两个都是以下结果
但是,输入1’时,出现的是另外结果

输入1,1″时,SQL语句没有报错,只是提示我们输入的值是不对的,而输入1’时却有截然不同的结果,因此我们可以先假设SQL语句闭合方式是单引号

注:以下sql语句如’1″ ‘ 实际上是’1″‘ ,这里只是为了快速区分

(1)一般情况下,SQL语句闭合方式为单引号

当用户名为1时,形成的sql语句是 select * from table_name where username=’1′ and password=’123′;

当用户名为1″时,形成的sql语句是正确的 select * from table_name where username=’1″ ‘ and password=’123’;

当字符串内需要包含双引号时,除了使用转义字符外,也可以使用一对单引号来包括字符串。此时字符串内的双引号被视为普通字符,无需特殊处理

同理,当字符串内需要包含单引号时,除了使用转义字符外,也可以使用一对双引号来包括字符串。此时字符串内的单引号被视为普通字符,无需特殊处理

补充

使用(转义字符)来判断SQL注入的闭合方式 原理:当闭合字符遇到转义字符时,会被转义,那么没有闭合符的语句就不完整了,就会报错,通过报错信息我们就可以推断出闭合符。

分析报错信息:看\斜杠后面跟着的字符,是什么字符,它的闭合字符就是什么,若是没有,则为数字型。 (但是在本题中有两个变量,这个方法不太适用)

当用户名为1’时,形成的sql语句是错误的 select * from table_name where username=’1′ ‘and password=’123’;

第一个单引号和第二个单引号形成了新的闭合,剩余第三个单引号,组成的sql语句不正确,于是语句报错

因此,SQL语句闭合方式是单引号

(2)假设MySQL语句为双引号闭合

username输入1时,形成的sql语句是正确的 select * from table_name where username=”1″and password=”123″;

username输入1″时,形成的sql语句是 select * from table_name where username=”1″ “and password=”123”; 正确的SQL语句不可以出现一对双引号包含双引号的。所以上面这条应该出现SQL报错,但实际没有报错,因此我们假设的双引号闭合方式是不成立的

username输入的是1’,形成的sql语句是正确的,不会报错 select * from table_name where username=”1′ “and password=”123”; 而然实际上这条语句报错了,因此我们假设的双引号闭合方式是不成立的

综上,我们可以推出SQL语句闭合方式是单引号

进行SQL注入

万能账号密码获取入口: 万能账号密码使用详解,渗透测试常用的入门级操作 – 知乎

由上得:该数据库的闭合方式为单引号

因此,在理解完以上网页内容后,我们回归该题

当我们不知道用户的账号并且不知道用户的密码时,可以使用万能账号 我们随便挑选一个——a or true #

但是它上面的万能账号都没有加单引号’或是双引号”

因此,综上,我们需要在a后面加上本题的闭合符号,“ ‘ ” 得出我们所需的账号为a’ or true #

密码随便输即可(但是看到其他人通常这种情况都直接写一样的,不知道有啥玄机在里面,这里注意一下)

复制,回去题目提交flag

补充做法(使用HackBar,不过跟上面的大差不差)

自行下载HackBar V2

前提: 先看测试后的网址(与为刚从靶机点进来的网址进行对比),可以看到我们输入的账号密码都显示在url中,可知此处是get传参

补充:get传参——参数和 URI 之间用问号?隔开, 参数键值用等号=连接,然后参数之间用连接符&拼接起来

因为get传参为参数直接暴露在 URL 中,且网页文件名为check.php,只是处理登录校验的常见文件名,而非诸如 login.php(通过表单 POST 传参)等需要动用其他工具,则可直接使用HackBar 修改参数
点击启动靶机后给出的网址,弹出页面,按F12,选择HackBar V2(绿色图标那个)
按一下Load URL,即可获取当前页面的网址 然后随便输入用户名密码,进去后看看网址
我们直接看测试后多出的网址

username=后面跟的是账号

password=后面跟的是密码

因此,我们根据上面说的思路,把万能账号填进去,密码随便填,构造一下,复制

check.php?username=a' or true %23& password=1 

然后填进Load URL得出的网址后面,点击Execute执行

解释:%23是#的 URL 编码形式,表示普通字符#, 在 URL 中,某些字符有特殊含义,需要使用URL 编码(即百分号编码)来表示, 类似于进制统一一样,为了避免不必要的麻烦,我们通常都把格式统一一下,

以下字符在 URL 中可直接使用,无需编码: 字母:a-zA-Z 数字:0-9 部分符号:-_.~ 但是遇到特殊字符的时候,最好去查一查看下要不要转变成URL

得出flag

2、极客大挑战 2019 Havefun(简单分析,HackBar)

题目

做法

启动靶机,点进去

常规F12看看源代码
发现这里有被注释了的代码

<!--
        $cat=$_GET['cat'];
        echo $cat;
        if($cat=='dog'){
            echo 'Syc{cat_cat_cat_cat}';
        }
        -->

简要分析一波

前提小知识

$cat $:表示一个自定义的普通变量 $+字符串:表示一个变量名/对象名 双$:表示一个可变变量,用于存储变量的值

$_GET $_GET 是 PHP中预定义的 超全局变量(Superglobal Variable),用于获取通过 HTTP GET请求发送的参数。 数据类型:本质是一个 关联数组,键为参数名,值为参数对应的值

说人话:如题中的

$cat=$_GET['cat'];

就是定义一个叫cat的变量,然后通过 $_GET 从URL参数中获取cat变量的值

echo $cat: echo PHP中有两个基本的输出方式:echo和print echo和print的区别: 1、echo – 可以输出一个或多个字符串 2、print – 只允许输出一个字符串,返回值总为 1 注: echo 是一个语言结构,使用的时候可以不用加括号,也可以加上括号: echo 或 echo()

Syc{cat_cat_cat_cat},类似于我们要的flag格式flag{},猜想就是满足上面的条件即可得出我们要的flag

$cat = $_GET['cat']; // 从URL参数中获取cat变量的值 
echo $cat; // 直接输出cat变量的值(存在XSS风险) 
if($cat == 'dog'){ // 判断cat变量是否严格等于字符串'dog' 
    echo 'Syc{cat_cat_cat_cat}'; // 如果条件成立,输出flag 
}

方法一

直接在URL上编写payload 根据上面的分析,我们直接在地址后面加上

/?cat=dog

然后回车,我们就可以得出flag

解释:

(1)

不带斜杠:服务器不知道你到底要访问首页,还是整个站点的根? 让其误解路径,导致参数传不到代码里 带斜杠:就是访问站点的根路径,然后还写入了cat=dog的参数

(2)
  • 满足 $_GET 接收逻辑:必须用 ?参数名=值 的格式,代码才能通过 $_GET['cat'] 拿到值。
  • get传参——参数和 URI 之间用问号?隔开, 参数键值用等号=连接,然后参数之间用连接符&拼接起来

    方法二(思路大差不差,不过利用了一下HackBar)

    F12后点击HackBar V2跳出此页面

然后点击左边的Load URL即可把当前的URL放进框中

然后就像上面的思路一般,直接在后面加入

/?cat=dog

点击Execute,即可得出flag

最后,回到题目提交flag即可

3、ACTF2020 新生赛 Include(phpfilter伪协议)

题目

做法

启动靶机,点进去

点进去
查看URL,有

?file=flag.php

说明存在文件包含,原理是php://filter 协议

当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容当普通文本返回(不执行PHP代码),这样就能拿到文件源码 ,实现任意文件读取。(这也是后面解释为什么要加base64编码的原因)

就是正常包含PHP文件执行时,代码会被执行,我们就看不到我们想要的东西 而通过php://filter,我们就可以把代码当做文本返回,而不是被执行,这样我们就可以得到我们想要的东西了

至此,我们就可以构造payload

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

解释: 1.**`php://filter

  • PHP 内置的数据流过滤器协议,允许在文件读写时对内容进行转换。
  1. read=convert.base64-encode
    • 过滤器参数:将读取的文件内容进行 Base64 编码后返回。
  2. resource=flag.php
    • 目标文件:指定要读取的文件路径(这里是flag.php)。

`php://filter是格式 read 这里是可选参数,有read和write,字面意思就是读和写,如果不写,那么网页会自动匹配一个合适的read或write: convert.base64-encode 是过滤器。主要有四种:字符串过滤器,转换过滤器,压缩过滤器,加密过滤器。filter里可以用一或多个过滤器(中间用|隔开),这也为解题提供了多种方法,灵活运用过滤器是解题的关键。这里的过滤器是把文件flag.php里的代码转换(convert)为base64编码(encode) resource** 是必选参数,后面写你要处理的文件名

URL修改后得出结果

复制下来,去解码,这里推荐一个(信息源于互联网,风险自担) Base64 编码/解码 – 锤子在线工具

得出结果,复制去提交即可

4、HCTF 2018 WarmUp(代码审计)

题目

做法

启动靶机,点进去

没啥信息,点开F12康康源码
发现有被注释的 在URL后面加进去被注释的东西,访问一下
分析一波

1. 代码起始和高亮显示

<?php
    highlight_file(__FILE__);
  • <?php:PHP 代码的起始标记。
  • highlight_file(__FILE__)
    • highlight_file() 是 PHP 内置函数,用于高亮显示文件的源代码(以 HTML 格式展示)。
    • __FILE__ 是 PHP 的魔术常量,表示当前文件的完整路径
    • 这行代码会把整个 PHP 文件的源代码显示在浏览器中(通常用于调试)。

2. 定义类和检查函数

    class emmm
    {
        public static function checkFile(&$page)
        {
  • class emmm:定义一个名为 emmm 的类(类似 Java/C++ 中的类)。
  • public static function checkFile(&$page)
    • public:函数可以被外部访问。
    • static:函数属于类本身,无需创建对象即可调用(如 emmm::checkFile())。
    • 静态方法checkFile用于检查文件
    • &$page引用传递参数,直接修改原始变量的值(这里未实际修改)。

3. 白名单设置

            $whitelist = ["source"=>"source.php","hint"=>"hint.php"];
  • 创建一个关联数组(类似 Python 的字典),白名单中只允许两个文件:
    • source.php
    • hint.php

4. 检查参数是否合法

            if (! isset($page) || !is_string($page)) {
                echo "you can't see it";
                return false;
            }
  • isset($page):检查变量 $page 是否存在且不为 NULL
  • is_string($page):检查变量 $page 是否为字符串类型。
  • 如果 $page 不存在或不是字符串,输出错误信息并终止检查。

5. 第一次白名单检查

            if (in_array($page, $whitelist)) {
                return true;
            }
  • in_array($needle, $haystack):检查 $needle 是否在 $haystack 数组中。
  • 如果 $page 直接等于 source.php 或 hint.php,允许访问。

6. 截取问号前的内容再检查

            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
  • mb_strpos($page . '?', '?')
    • mb_strpos() 查找字符串中第一次出现 ? 的位置。
    • $page . '?' 在字符串末尾添加 ?,防止找不到 ? 时返回 NULL
  • mb_substr($page, 0, ...):截取 $page 从开头到 ? 之前的部分。
  • 例如:$page = "source.php?xxx" → $_page = "source.php"
  • 如果截取后的内容在白名单中,允许访问。

7. URL 解码后再次检查

            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . '?', '?')
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
  • urldecode($page):对 URL 编码的字符串进行解码(例如 %20 → 空格)。
  • 再次截取解码后的字符串中 ? 之前的部分,检查是否在白名单中。
  • 例如:$page = "source.php%3Fxxx"%3F 是 ? 的 URL 编码)→ 解码后 $_page = "source.php"

8. 检查失败处理

            echo "you can't see it";
            return false;
        }
    }
  • 如果所有检查都不通过,输出错误信息并返回 false

9. 主程序:接收用户输入并包含文件

    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']:获取用户通过 URL 参数(如 ?file=xxx)或 POST 表单提交的 file 参数。
  • empty():检查变量是否为空(值为 NULL'' 等)。
  • is_string( ):检查变量是否为字符串类型。
  • emmm::checkFile($_REQUEST['file']):调用之前定义的检查函数。
  • include $_REQUEST['file']
    • include 是 PHP 的文件包含函数,将指定文件的内容嵌入到当前脚本中执行 即先暂停主脚本的运行,先运行子脚本 (1)若内容是php的话,直接运行此php文件,完成后再跳出来继续运行主脚本(假设include里的脚本为子脚本) (2)若里面含有echo等输出函数、被引入文件包含 HTML 或非 PHP 内容(eg.纯文本内容)、return、被引入文件包含错误或警告信息等,则会直接读取并输出相应内容
    • 风险:如果检查函数存在漏洞,攻击者可能通过构造特殊的 file 参数读取任意文件。
  • exit:终止脚本执行,防止后续代码被执行。
  • 如果条件不满足,显示一张默认图片。

综上,我们为了绕过上面的保护,只需要在URL后写入上面提到hint.php(自行尝试,source.php那个我试了不行,没有提示我们要的flag在哪)

得出,我们要的flag就在ffffllllaaaagggg这里

至此,我们编写payload

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

解释:

1、为什么php后要加“ ?”呢:

(1)为了应付上面的检查,不然它就会在ffffllllaaaagggg后面加一个?我们就无法通过检测 (2)为了应付include函数,如下

2、include函数解释(以下通过上面给的第一个payload加以解释)

第一步:绕过白名单

传进 file= 的是: hint.php?/../../../../ffffllllaaaagggg PHP 会提取 ? 前的部分(hint.php),发现它在白名单中,就放行了。

第二步:PHP include 怎么处理这个路径?

include('hint.php?/../../../../ffffllllaaaagggg'); 这是 PHP 的语法。

PHP 遇到 include("xxx?yyy") 的时候,只会 include xxx 这个文件,不会理会 ?yyy。 所以,PHP 实际执行的是: include("hint.php");

到这里你是读取 hint.php 并执行它了!

第三步:hint.php 的作用

虽然你没看到 hint.php 的源码,但你可以合理推测它会这样写<include($_SERVER['QUERY_STRING']);// 它把 ? 后面的东西当成路径去 include> 你不知道你要找的flag在哪个文件里,一定会在这俩白名单文件里吗?谁也说不准,最简单的方法就是回到根目录进行查找了

你传的这个: source.php?file=hint.php?/../../../../ffffllllaaaagggg 会让 hint.php 里的 $_SERVER['QUERY_STRING'] 变成: /../../../../ffffllllaaaagggg 于是 hint.php 执行了: include("/../../../../ffffllllaaaagggg"); // 最终解析为 /ffffllllaaaagggg 第二次 include() 是在 hint.php 中执行的,相对路径会从 hint.php 所在的目录 /var/www/html 开始解析,层层 ../ 向上,最终到达根目录 /,再拼上目标文件名,形成有效路径。

详细一点: 这里的include是这样工作的——假如hint.php的绝对路径是/var/www/html/hint.php,然后../就会变成/var/www/html,再../就会继续返回上一级/var/www,以此类推,最终返回根目录,即是/ffffllllaaaagggg了,就像你直接在你的c盘找一个文件一样,你不知道它放在哪,因此直接返回根目录一搜,即可搜到,因此,其实上面的payload有多少个../都没什么问题,但是一定不能少,根目录再上一级还是停留在根目录上,不会再改变

那还有一个问题,就是payload里的../最前面的/还有必要加吗? 我这边尝试了一下,发现去掉这个/还是可以得出flag,大概是无所谓的 但这里建议还是带上为好,因为带 / 更安全更规范,防止被解析成查询参数而不是路径字符串,从而导致我们得不到flag而去怀疑自己思路,导致更进一步的错误

3、如果没有那个 / 会怎样?(URL上需要加/)

如果你传: hint.php?../../../../ffffllllaaaagggg PHP 会以为你传的是个 GET 参数名叫 ../../../../ffffllllaaaagggg,值为空: 在不同 PHP/服务器组合中,有的解析器只有以 / 开头才会处理为路径 因此,这里强烈建议加上/,这里指的是URL上的

类型 意义
路径 控制服务器访问哪个资源
参数 把值传给正在运行的脚本

要是识别成参数,就变成是写进去东西了,而不是访问/找某个文件,跟我们的目的相悖了

4、但在include函数上呢,则恰恰相反了,是不建议加/的

它截取的是payload的?后面的,刚好把/截取到 一般来说,有/的是绝对路径,没有/的是相对路径

假设你的文件在这里: /var/www/html/hint.php 你想 include: /flag 那你可以这样: include("../../../../flag"); // 从 hint.php 的目录出发 // 即:/var/www/html/../../../../flag = /flag 你也可以: include("/flag"); // 绝对路径 都没问题。

但是你如果写:(可以,但没必要) include("/../../../../flag"); // 从根目录开始,还向上跳❓ PHP 实际等价于: /../../../../flag => /flag 虽然结果一样,但这写法:

  • 没有逻辑意义:根目录不能再往上跳
  • 容易引起误解或报错
  • 在不同系统/配置上行为不一致(特别是 open_basedir 或安全策略存在时)

5、那还有一个问题,就是既然上面说的

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

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

都可以得出flag,那我直接

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

行不行呢,上面说了嘛,既然直接从根目录出发这样写都可以,那我何必还要写这么多../呢 直接/,一步到位,岂不美哉?

但答案是不行的,至少在该题中找不到flag

include("/flag")

大概是路径解析过程中触发了安全限制/被限制了,因此行不通

include("/../../flag")

一般是失败的,跟上面一样 (/../../flag其实跟/flag一样的,”/”继续往上还是”/”因此后面的../其实没什么作用)

但如果路径被错误解析,则可能绕过限制(就像最前面没加/的情况一般) 因此,这种情况行不通的情况下,我们就得去掉最前面的/了

一旦路径以 / 开头,就告诉 PHP: “我给的是绝对路径,不用管当前文件在哪。”

  • some/path/to/file → 相对路径(从当前工作目录出发)
  • /some/path/to/file → 绝对路径(从根 / 出发)

没办法了,找了半天,最合理的解释也只能是这样了【单纯指的include(“/flag”)和include(“/../../flag”)】,平时做题/比赛的时候不行就按照上面的思路一个一个慢慢试吧

得出flag

复制,回去提交

感谢阅读!如果你觉得这篇文章对你有帮助,欢迎扫码赞赏支持,你的鼓励是我持续创作的动力 ❤️

本文为原创内容,转载请注明出处。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇