从0到1,从无到有……
开始涉足代码审计,了解了一些敏感函数之后,是时候拿点实例来练练手了!

把握大局(分析网站结构)-> 通读全文法/敏感函数参数回溯法/定向功能分析法 ->


文件上传

move_uploaded_file

用sexy法师的审计系统扫一扫,发现存在四个地方存在文件上传(move_uploaded_file 函数)

1.admin_pic.php

这个只是在图片列表“修改”选项处的重新上传,并将旧照片替换掉,用burp改参数的确可以直接传一个php文件,但是后缀名是不变的,所以没有利用的地方。

2.fun.php

定义函数的汇总 php。这里定义了一个 up_img 函数,规定了大小、格式、是否重名……

同样的,上传这一关很容易就bypass过去了。但是仔细审计 up_img 函数就会发现。它的 $file_name 不是直接就从上传那直接获得,而是经过一系列的拼接,估计就是为了防止稍微用burp改一下参数就导致文件上传漏洞的产生吧!

3.img.class.php

这里也有一些关于图片信息的函数,文件名是由“年月日时分秒+id(rand)+拓展名(ext)”拼接而成

4.upload_more.php

上传多张图片
在upload.php中测试怎么bypass

经过上述php的审计,得出他是用白名单策略来检查扩展名,所以就试了下 nginx空子节漏洞,用burp在.jpg后加上%00.php后发现上传的jpg文件已经被解析为php了。所以…. :)

难道只存在解析漏洞?再找找看~~

但是我又在之前已嵌入php代码的png文件中尝试用%00.php 来执行png中的代码,发现不得行:

初步分析是过滤了%
但是直接在url上加%后enter,发现,没有被转义啊!

But Why?

getimagesize()

验证文件头只要为GIF89a,就会返回真

1.admin_pic.php

if($is_thumb){//开启缩略图
        $file_info=@getimagesize($file_name);
        if(empty($file_info)){msg('图片不存在,操作失败');}

2.fun.php

if(!move_uploaded_file($file['tmp_name'],$file_name)){
            msg('图片上传失败','',0);
        }
        $file_info=@getimagesize($file_name);
            switch($file_info[2]){
            case 1:
            $php_file=@imagecreatefromgif($file_name);
            break;
            case 2:
            $php_file=@imagecreatefromjpeg($file_name);
            break;
            case 3:
            $php_file=@imagecreatefrompng($file_name);
            break;
            }

上传图片下面有getimagesize()函数,对参数进行处理。

if($_sys['image_type'][0]){
                $logo=CMS_PATH.'upload/'.$_sys['pic'];
                $logo_info=@getimagesize($logo);
                switch($logo_info[2]){
                case 1:
                $logo_file=@imagecreatefromgif($logo);
                break;
                case 2:
                $logo_file=@imagecreatefromjpeg($logo);
                break;
                case 3:
                $logo_file=@imagecreatefrompng($logo);
                break;
                }

虽然加了png文件头,识别为图片后缀。但是想绕过上传,怕是不大行,因为都是用同一套上传验证机制。这个函数好像在这里好像只是用于读取长宽,并判断后缀名,下面也对postion位置进行case分类[1-2-3],然后拼接。用imagecopy拷贝一下图像参数。

imagecopy($logo_img,$logo_file,$position[0],$position[1],0,0,$logo_info[0],$logo_info[1]);

getimagesize 函数不是完全可靠的

文件包含

遍历了所有包含include的文件,全都是直接拼接的路径like “ include(DATA_PATH.$lang.'_info.php'); ",没有发现包含来自($_GET or $POST )来的文件,所以。。在当前的知识框架下,我好像找不到其他利用的方式,也就无法配合伪协议+文件上传漏洞来利用。

文件操作

任意文件读取

先从 file_get_contents() 入手

1 -> karma@d0g3.cn.php

$privKeyStr = file_get_contents($this->DKIM_private);

DKIM是域名密钥识别邮件标准 ,所以此处应该只是将隐私信息赋给 $privkeystr ,没有发现可控的地方。

```Php
$file = tempnam('', 'karma@d0g3.cn');

file_put_contents($file, $body); //TODO check this worked

$signed = tempnam("", "signed");
if (@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)) {

@unlink($file);

@unlink($signed);

$body = file_get_contents($signed);
```

$signed = tempnam("", "signed");

// tempnam(dir,prefix) 创建一个具有唯一文件名的临时文件。

@openssl_pkcs7_sign($file, $signed, "file://".$this->sign_cert_file, array("file://".$this->sign_key_file, $this->sign_key_pass), NULL)

// openssl_pkcs7_sign —> Sign an S/MIME message 好像是一个类似数字签名验证的东西

$signed 里面的数据是在验证中从 $file 中传过去的,而 $file <- sign_cert_file ,好像也没有可控的变量 :(

$file_buffer = file_get_contents($path);

$file_buffer = $this->EncodeString($file_buffer, $encoding); // base64转义

2 -> admin_db.php

elseif($action=='save_import'){
    if(!check_purview('data_import')){msg('<span style="color:red">操作失败,你的权限不足!</span>');}
    $fl = $_GET['fl'];
    $time = isset($_GET['time'])?$_GET['time']:'';
    $rand = isset($_GET['rand'])?$_GET['rand']:'';
    $page = isset($_GET['page'])?$_GET['page']:1;
    if(empty($fl)){err('<span style="color:red">参数传递错误,请重新操作</span>');}
    if($time&&$rand&&$page){
$import_file =DATA_PATH.'backup/'.$fl.'/db_backup_'.$time.'_'.$rand.'_'.$page.'.sql';
    if(@file_exists($import_file))
    {
        $data=@file_get_contents($import_file);
        $data=explode(";\n",trim($data));

        if(!empty($data)){
            foreach($data as $k=>$v){
                $GLOBALS['mysql']->query($v);
            }
        }

        show_htm('卷'.$page.'导入完成','?action=save_import&fl='.$fl.'&time='.$time.'&rand='.$rand.'&page='.($page+1).'&nav='.$admin_nav.'&admin_p_nav='.$admin_p_nav);
    }

$file_get_contents <- $import_file <-(DATA_PATH.'backup/'.$fl.'/db_backup_'.$time.'_'.$rand.'_'.$page.'.sql')

其中 $f1$time$rand$page都是通过 $_GET 传递的,所以,是可控的,看看是否有利用的价值。

三元运算符: ?:

```php
<?php
// Example usage for: Ternary Operator
$action = (empty($_POST['action'])) ? 'default' : $_POST['action'];

// The above is identical to this if/else statement
if (empty($_POST['action'])) {
$action = 'default';
} else {
$action = $_POST['action'];
}
?>
```
explode() 把字符串打散为数组

trim() 移除字符串两侧的字符

3 -> tpl.class.php

    if($include_cache){$this->template_is_cache=0;}
    $this->template_file=$this->template_dir.$template_file.'.html';
    if(!file_exists($this->template_file)){die('不存在模板文件'.$template_file.'.html');}
    $this->template_file_content=file_get_contents($this->template_file);
    $this->template_com_file=$this->template_compile.$template_file.'_'.$this->template_lang.$this->mb.'_compile.php';//编译文件
    $this->cache();//生成缓存文件
    $this->compile();//编译模板
    $this->view($is_copy);//显示模板
    if($this->tp){echo $this->content;}

$this->template_file <- $this->template_dir.$template_file.'.html'未发现可控变量,而且模版是直接从数据库中调出,并没有上传模版的地方。

再从 fread() 入手

1 -> fun.php

function copy_lang($lang){
    if(isset($lang)){
        if(!$fp=@fopen(LANG_PATH.'lang_cn.php','r')){
            msg('基本语言包不能操作,请检查是否有操作文件的权限','javascript:history.go(-1);');
        }
        $fl=fread($fp,filesize(LANG_PATH.'lang_cn.php'));
        unset($fp);
        $fp2=@fopen(LANG_PATH.'lang_'.$lang.'.php','w');
        return fwrite($fp2,$fl);

function creat_inc($fl,$str){
    if(file_exists($fl)){@unlink($fl);}
    if(!$fp=@fopen($fl,'w')){
        msg('文件打开失败,请检查是否有足够的权限操作文件');
    }
    flock($fp,LOCK_EX);
    if(!fwrite($fp,$str)){
        msg('写入文件失败,请检查是否有足够的权限操作文件');
    }
    flock($fp,LOCK_UN);
    unset($fp);

生成语言配置文件,好像。。没办法从基本语言包处入手啊!

2 -> admin_template_php

elseif($action=='xg'){
    if(!check_purview('tpl_manage')){msg('<span style="color:red">操作失败,你的权限不足!</span>');}
    $file = $_GET['file'];
    $path=CMS_PATH.$file;
    if(!$fp=@fopen($path,'r+')){err('<span style="color:red">模板打开失败,请确定【'.$file.'】模板是否存在</span>');}
    flock($fp,LOCK_EX);
    $str=@fread($fp,filesize($path));
    $str = str_replace("&","&amp;",$str);
    $str= str_replace(array("'",'"',"<",">"),array("&#39;","&quot;","&lt;","&gt;"),$str);
    flock($fp,LOCK_UN);
    fclose($fp);
    include('template/admin_template_xg.php');

$path <- CMS_PATH.$file <-$_GET['file']

虽然变量可控,但是好像没有echo的地方啊,也无法形成任意文件读取漏洞~ :(

3 -> karma@d0g3.cn.php
多个PHP karma@d0g3.cn函数引发的命令执行漏洞分析

XSS

SQL Injection

1-> member.php

登陆的时候随便测试了一下万能密码,发现 \' 转义了,= 也被过滤了。

但是在burp上稍微改改参数,但是会显示密码不正确,好像还没看懂如何验证登录。

首页的搜索框是不是也可以看看有没有注入的点?

好像一有query语句,就会有x和y……

然后sql语句执行了有上90条~估计是遍历各个地方看看有没有所需的东西。

代码/命令执行

preg_replace()

popen()

  1. karma@d0g3.cn.php
if ($this->SingleTo === true) {
      foreach ($this->SingleToArray as $key => $val) {
        if(!@$karma@d0g3.cn = popen($sendmail, 'w')) {
          throw new phpmailerException($this->Lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
        }
        fputs($karma@d0g3.cn, "To: " . $val . "\n");
        fputs($karma@d0g3.cn, $header);
        fputs($karma@d0g3.cn, $body);
        $result = pclose($karma@d0g3.cn);

NOTES

payload、shellcode、exp、poc怎么区别?

php中$this->是什么意思?

PHP中的符号 ->、=> 和 :: 分别表示什么意思?

Nginx %00空字节执行任意代码(php)漏洞