当前位置: 首页 > news >正文

攻防世界 web

攻防世界 baby_web

解题

说到初始界面肯定是会想到index.php

image-20241208145504264

但是实际进入题目以后发现是1.php,在通过dirsearch扫描网页目录无果后尝试直接在url输入index.php,结果发现还是会自动跳转到1.php

image-20241208145450558

方法一

打开F12查看响应,发现index.php是302状态码,也就是重定向状态码

image-20241208150132448

补充

  1. index.php的状态是302什么意思?

302 Found,原始描述短语为 Moved Temporarily(临时搬家) ,是HTTP协议中的一个状态码(Status Code)。可以简单的理解为该资源原本确实存在,但已经被临时改变了位置;换而言之,就是请求的资源暂时驻留在不同的URI下,故而除非特别指定了缓存头部指示,该状态码不可缓存。

  1. 访问index.php跳转到1.php这种情况又是什么原理呢?

一般原网页被换地方后,有人访问该网页是会被自动定向到另一个设置好的网页,且临时URI应该由响应头部中的 Location 字段给出。

我们在响应头果然发现了它

image-20241208150523756

同时也就找到了flag

方法二

通过bp抓包,然后传入重发器修改1.php为index.php发送,即可在响应头看到flag

image-20241208150730299

攻防世界 robots

image-20241207201858335

倒是第一次见robots协议,不知道是啥,百度一下

image-20241207201942131

原来只要在网站url后面加个/robots.txt就出来了,思路加一

image-20241207202048181

打开网页白茫茫一片啥也没有,根据刚刚的思路读一下robots协议

image-20241207202143791

提示我们flag在f1ag_1s_h3re.php目录下,访问一下即可得到flag

image-20241207202226955

攻防世界 simple_js

解题

进入题目以后就是这么一个界面,尝试几个弱口令

image-20241208160512742

经过几次尝试后发现一直输出FAUX PASSWORD HAHA,页面源代码也是空的,dirsearch扫目录也没东西

尝试yakit抓包看看有什么东西没有

image-20241208160549930

可以看到一大串JavaScript代码,将其拷贝下来仔细阅读

image-20241208160723418


<html>
<head><title>JS</title><script type="text/javascript">function dechiffre(pass_enc){var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";var tab  = pass_enc.split(',');var tab2 = pass.split(',');var i,j,k,l=0,m,n,o,p = "";i = 0;j = tab.length;k = j + (l) + (n=0);n = tab2.length;for(i = (o=0); i < (k = j = n); i++ ){o = tab[i-l];p += String.fromCharCode((o = tab2[i]));if(i == 5)break;}for(i = (o=0); i < (k = j = n); i++ ){o = tab[i-l];if(i > 5 && i < k-1)p += String.fromCharCode((o = tab2[i]));}p += String.fromCharCode(tab2[17]);pass = p;return pass;}String["fromCharCode"](dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"));h = window.prompt('Enter password');alert( dechiffre(h) );</script>
</head></html>

代码漏洞

上面代码简单说就是将\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30作为pass_enc的值传入dechiffre函数中(pass_enc十六进制解码后得十进制数据为55,56,54,79,115,69,114,116,107,49,50),然后将pass_enc通过pass_enc.split(',')方法由字符串拆分为一个字符数组tab,中间由逗号分割开来,定义的pass变量进行同样操作,但是值得一提的是,后面通过两个for循环操作将tab2分割处理再拼接为p,将p赋值给pass再由页面回显这个过程,tab2是完全没有被提到和使用的,所以不过你在password处输入什么最后页面都只会回显FAUX PASSWORD HAHA

于此我们便推断tab2数组里的元素就是flag

代码审计的详细过程

执行流程:
一、首先定义了一个dechiffre函数,咱先不管,因为还没有调用
注:先将\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30十六进制数转换成字符串,python下print即可,或网址:https://www.bejson.com/convert/ox2str/
输出结果55,56,54,79,115,69,114,116,107,49,50二、执行String["fromCharCode"](dechiffre("55,56,54,79,115,69,114,116,107,49,50
"));三、调用了dechiffre,执行dechiffre函数
String["fromCharCode"](dechiffre("55,56,54,79,115,69,114,116,107,49,50
"));
(1)先将"55,56,54,79,115,69,114,116,107,49,50
"带入dechiffre函数执行,即dechiffre(pass_enc)=dechiffre("55,56,54,79,115,69,114,116,107,49,50
")(2)接着我们看到了pass变量,暂时先放着(3)因为pass_enc="55,56,54,79,115,69,114,116,107,49,50"
将pass_enc字符串分割成字符串数组,赋值给tab参数,所以:
tab=[55,56,54,79,115,69,114,116,107,49,50]   注:tab此时是字符串数组!!!(3)随后也对pass分割
tab2=[70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65](4)变量赋值代码分析:var i,j,k,l=0,m,n,o,p = "";i = 0;j = tab.length;
一开始i,j,k,m,n,o,没有赋值,为undefined,其它参数l=0,p="",后来i被赋值=0,j被赋值为11(5)第九行此时n被赋值为0,所以k=11+0+0,最后等于11    注:这里的(l)其中是英文字母l,不是数字1(6)第十行中,n=18(7)第一个for循环,精简一下代码:
for(i = 0; i < (18); i++ )
{o = tab[i-l];p += String.fromCharCode((o = tab2[i]));if(i == 5)break;}
解释:前面的o=tab[i-1]是无用的,因为后面会被o=tab2[i]的值重新覆盖
第一次循环:o=tab[0];p=p+String.fromCharCode((o = tab2[0])
=>o=70;p=""+String.fromCharCode(70)=>p=英文字母F
第二次...
第三次...
第四次...
第五次...
所以,这个for循环,最后的p为(尽管没有输出出来,这里我们知道就好)FAUX P(8)第二个for循环,精简一下代码:
for(i = 0; i < 18; i++ ){
o = tab[i-l];if(i > 5 && i < 17)p += String.fromCharCode((o = tab2[i]));
}
解释:这里的for循环和上面的差不多,注意这里的p值由于第一次for循环执行后现在已经是FAUX P了
加上第一次for循环的p值,最后的p为FAUX PASSWORD HAH(9)p += String.fromCharCode(tab2[17]);
因为tab2=[70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65]
所以:p=FAUX PASSWORD HAH + A
因此,最后的p为FAUX PASSWORD HAHA(10)pass = p;return pass;即 pass = FAUX PASSWORD HAHA;return FAUX PASSWORD HAHA;最后函数输出为FAUX PASSWORD HAHA嗯哼???这个函数就执行完了???我的tab数组怎么没有用到???,我一开始带进来的参数呢?去哪了?别想了,输出值虽然用到了带进来的参数(就是分割后的tab数组),但是for循环那里人家直接使用tab2数组相关代码的值,根本没有用到tab数组的值,所以由于代码逻辑问题,你传入的dechiffre的参数pass_enc是没有任何意义的三、dechiffre函数执行完成后,继续执行其它的代码
h = window.prompt('Enter password');alert( dechiffre(h) );
h=你输入弹框内的内容
之后alert弹出dechiffre(h)的值,由前面所有的代码可知,代码里p的值与tab无关,因为最终都会被tab2的值替代,所以我们无论输入什么,也就是pass_enc=h,无论输入的这个h等于什么,不管tab能否被分割成字符串数组,是否存在,都只会利用到tab2。通俗点讲,有关tab的参数与值都可以视为没有,因此,pass_enc参数是什么也就没有意义了四、最后,结论就是,无论我们在弹框中输入什么值,都只会返回FAUX PASSWORD HAHA
我就猜想,会不会String["fromCharCode"](dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"));这个语法错误,并且没有没计算出来的是不是最后正确的值,也就是flag~
于是,我不用它这么无论pass_enc参数输入什么都显示FAUX PASSWORD HAHA的函数,咱也抛弃它一回,自己重新写代码执行它

将tab2数组的值十六进制解码为十进制数后(十进制数其实对应的就是ascii码值),再ascii解码为字母即可得到flag

image-20241208163914728

image-20241208163834208

功防世界 unserialize3

进入题目,可以看到是一个很经典的php反序列化内容(看题目名字也是)

image-20241208093136379

但是仔细代码审计就会发现这里的括号没有闭合,下面还给了个?code=

合理猜测是要我们通过get类型传参传入序列化输出后的字符串

这道题里面用到了_wakeup()魔术方法,还是第一次见就写个wp记录一下

_wakeup()魔术方法

当使用 unserialize() 反序列化一个对象成功后,会自动调用该对象的 __wakup() 魔术方法。

该方法的原型如下

public function __wakeup()
{// 一些其它初始化操作
}

该魔术方法既没有参数,也没有返回值

如何绕过_wakeup()魔术方法

在此之前我们要了解_wakeup()魔术方法绕过原理,如果我们将对象包含属性个数写的比实际的多,那么多出来得到属性吗=名会被赋值为null,如果此时依然进行反序列化的话,可能会报错,为了防止这种情况的出现,php如果发现对象包含属性个数写的比实际的多,就会跳过_wakeup()魔术方法

image-20241208100121560

解题

<?
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
}
$a = new xctf();
echo(serialize($a));
?>

根据题目给的代码我们补全就能写出以上脚本,运行以后会得到一个字符串(序列化产生的字符串)

O:4:"xctf":1:{s:4:"flag";s:3:"111";}

我们将会对象属性个数由‘1’改为‘3’(这里随便写,比1大就行),再通过code参数传入网页即可出flag

image-20241208104020109

攻防世界 warmup

进入题目

进来就是大大的一个滑稽脸,不得不看看源码了08FBE0AD

image-20241210145306116

源码如下,访问一下source.php,我猜估计又是代码审计08FDC914

代码审计

image-20241210150910350

<?phphighlight_file(__FILE__);class emmm{public static function checkFile(&$page)//检测传入的page参数函数{$whitelist = ["source"=>"source.php","hint"=>"hint.php"];//这是白名单if (! isset($page) || !is_string($page)) {//检测传入的page参数是不是字符串,不是就返回"you can't see it"echo "you can't see it";return false;}if (in_array($page, $whitelist)) {//检查传入的page参数符不符合白名单return true;}$_page = mb_substr(//这里需要mb_strpos函数返回的参数,这个函数的大概意思就是$page,0,mb_strpos($page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}$_page = urldecode($page);$_page = mb_substr($_page,0,mb_strpos($_page . '?', '?')//mb_strpos函数找到$page中第一个问号的位置,然后使用mb_substr函数将问号之前的部分作为$_page进行处理(ps:说明:符号点 '.' 是 PHP 中的字符串连接运算符,它用于将两个字符串连接在一起,形成一个更长的字符串)在这里,它将 $page 变量的值和一个问号字符 '?' 连接在一起,形成一个新的字符串,在这个新的字符串中查找问号是否存在,那么很明显肯定能找到.也就是说 in_array() 函数的第三个参数length肯定为正数,又因为in_array() 函数的第二个参数start为0,因此会在字符串中的第一个字符处开始按照length长度进行截取,重新赋值给page);if (in_array($_page, $whitelist)) {//再次检查page参数是不是符合白名单return true;}echo "you can't see it";//不是白名单就返回输出"you can't see it"return false;}}if (! empty($_REQUEST['file'])//如果传入的file参数不为空&& is_string($_REQUEST['file'])//如果file参数后面接的是字符串&& emmm::checkFile($_REQUEST['file'])//如果传入的file参数经过checkfile函数检查通过以后) {include $_REQUEST['file'];//这里非常关键,我们始终是在source.php页面下进行操作,这里我们运用include函数将flag文件包含出来exit;} else {echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";}  
?>

构造payload

source.php?file=hint.php?/../../../../ffffllllaaaagggg
source.php?file=source.php?/../../../../ffffllllaaaagggg

为什么构造成这个样子092C35B9

传入file=hint.php,首先检查'hint.php'是否是一个字符串,它是字符串,条件通过;

检查'hint.php'是否在白名单中(白名单包括hint.php和source.php),在,继续执行后面的代码;

对'hint.php'执行mb_substr函数,但是函数内一个参数是来自另一个函数mb_strpos的返回值,因此我们先看mb_strpos函数,使用.进行字符连接,即连接了一个问号字符 '?',得到hint.php?

然后查找'?'在字符串'hint.php?'中第一次出现的位置,从0开始算,返回8,即length=8

接下来我们执行mb_substr函数,即 mb_substr('hint.php',0,8)

从字符串中的第一个字符处开始,返回8个字符,其实还是返回的hint.php;

然后对返回的内容进行url解码,重复执行上面的检查和截取操作。

我们只需要传入一个在白名单内的文件名(source.php或者hint.php),并添加上问号,这样可以保证每次找去用于检查的内容都在白名单,返回true。

至于说为什么要写成../../../../,因为hint.php一般是在/var/www/html目录下面的,而且有个

hint.php了所以还需要多返回一个上级目录

PS:后面看了别人的博客·,有人说ffffllllaaaagggg各有四个字母其实也就是提醒你返回四个上级目录

攻防世界web PHP2

进入题目看到这样一个界面,毫不犹豫直接用御剑扫描一下目录

image-20241208210246107

由于忘记保存了,这里直接告诉读者扫出一个index.phps目录,一般phps就是php页面源代码保存的地方(如果该php页面没有页面源代码)

image-20241208215830657

接下来就是代码审计了,如果admin和get传参的id参数值强比较相同,那么页面就会返回 not allowed!

将id参数值url解码以后再与admin比较,如果相同就会输出flag

因为_GET本身有一次urldecode,加上代码中$_GET[id] = urldecode($_GET[id]);语句又一次urldecode解码,因此有两次解码。所以我们要URL后加

1 ?id=%2561dmin

因为当传入参数id时,浏览器在后面会对非ASCII码的字符进行一次urlencode

然后在这段代码中运行时,会自动进行一次urldecode

在urldecode()函数中,再一次进行一次解码

urldecode(%2561)=%61``urldecode(%61)=a

即,当第一次比较时,实际是


if("admin"==="%61dmin") 

而经过

$_GET[id] = urldecode($_GET[id]);

第二次比较是:

if("admin" == "admin");

这样就可以输出flag了(记得是在index.php页面传参不要傻傻的在index.phps页面传参啊)

image-20241208220630666

http://www.hskmm.com/?act=detail&tid=10623

相关文章:

  • 批判 vs 审判
  • XXL-JOB-源码分享(1)
  • ctfshow web入门 SSRF
  • C#中避免GC压力和提高性能的8种技术
  • ctfshow web入门 爆破
  • 函数内联
  • 7. Innodb底层原理与Mysql日志机制深入剖析
  • 深入解析:HSA35NV001美光固态闪存NQ482NQ470
  • ERP和MES、WMS、CRM,到底怎么配合 - 智慧园区
  • YOLO实战应用 1YOLOv5 架构与模块
  • YOLO实战应用 2数据准备与增强
  • Day18稀疏数组
  • 底层
  • YOLO实战应用 3训练与优化策略
  • WPF 视图缩略图控件(支持缩放调节与拖拽定位)
  • ik中文分词器使用
  • 动态水印也能去除?ProPainter一键视频抠图整合包下载
  • SpringBoot整合RustFS:全方位优化文件上传性能
  • windows使用es-client插件
  • AI学习日记 - 实践
  • es中的端点
  • 解码C语言宏
  • es中的索引
  • es中的数据类型
  • 防御安全播客第214期:数据泄露与漏洞攻防实战
  • windows使用kibana
  • 03作业
  • 软工作业个人项目
  • YOLO进阶提升 5标注与配置
  • rapidxml中接口函数