URL编码的一些思考
关于URL编码和简单的绕过的思考
本文仅作为我自己的学习过程的记录
前言:一直以来,我对于URL编码一直都不太感冒,虽然大概了解是怎么编码的,但总还是差点感觉(可能是刚刚入门,太菜),直到我遇到一个简单的绕过题,很适合去了解URL编码的一些知识.
例题
打开页面发现如图:
尝试输入数据,无果,只好先查看源码,发现可疑的链接如图
<body>
<div class="loginform cf">
<form name="login" action="index.php" method="POST" accept-charset="utf-8">
<ul>
<li>
<label for="SMILE">请使用微笑过关<a href="?view-source">源代码</a></label>//可疑的链接
<input type="text" name="T_T" placeholder="where is your smile" required>
</li>
<li><input type="submit" value="Show"> </li>
</ul>
</form>
</div>
</body>
尝试和主URL拼接,成功得到一段PHP源码又是PHP代码审计题
<?php
header("Content-type: text/html; charset=utf-8");
if (isset($_GET['view-source'])) {
show_source(__FILE__);
exit();
}
include('flag.php');
$smile = 1;
if (!isset ($_GET['^_^'])) $smile = 0; //判断^_^参数是否存在
if (preg_match ('/\./', $_GET['^_^'])) $smile = 0; //^_^参数的值中没有'.'
if (preg_match ('/%/', $_GET['^_^'])) $smile = 0; //^_^参数的值中没有'%'
if (preg_match ('/[0-9]/', $_GET['^_^'])) $smile = 0; //^_^参数的值中没有数字
if (preg_match ('/http/', $_GET['^_^']) ) $smile = 0; //^_^参数的值中没有字符串'http'
if (preg_match ('/https/', $_GET['^_^']) ) $smile = 0; //^_^参数的值中没有字符串'https'
if (preg_match ('/ftp/', $_GET['^_^'])) $smile = 0; //^_^参数的值中没有字符串'ftp'
if (preg_match ('/telnet/', $_GET['^_^'])) $smile = 0; //^_^参数的值中没有字符串'telent'
if (preg_match ('/_/', $_SERVER['QUERY_STRING'])) $smile = 0; //查询字符串中没有字符'_'
if ($smile) {
if (@file_exists ($_GET['^_^'])) $smile = 0; //以^_^参数的值为文件名的文件不存在
}
if ($smile) {
$smile = @file_get_contents ($_GET['^_^']); //将^_^参数的值以字符流读入$smile
if ($smile === "(●'◡'●)") die($flag);
}
?>
总的来说:
1.必须对"^_^"赋值
2."^_^"的值不能有 . % [0-9] http https ftp telnet 这些东西
3.$_SERVER['QUERY_STRING'],即"^_^=(输入的值)"这个字符串不能有 _ 这个字符
4.满足$smile!=0
5.file_exists ($_GET['^_^'])必须为0.也就是$_GET['^_^']此文件不存在
6."$smile"必须等于"(●'◡'●)".也就是file_get_contents($_GET['^_^'])必须为"(●'◡'●)",即和5矛盾,文件不存在,又要有数据
其他的都是简单,主要就是3和5,6的绕过
5,6的绕过可以通过data伪协议来从外界读入数据而又不通过文件(data:,(●’◡’●))
这里我们重点关注3的绕过
首先
$_SERVER[‘QUERY_STRING’]
query string(查询字符串),如果有的话,通过它进行页面访问。 简单来说就是用于返回用户实际输入的GET请求的参数以及参数值
<?php
header("Content-type: text/html; charset=utf-8");
if (isset($_GET['^_^'])){
echo 'it is get';
echo '<br>';
}
if(isset($_POST["name"])){
echo 'it is post';
echo '<br>';
}
echo $_SERVER['QUERY_STRING'];
?>
看了其他大佬的wp有两种解题方法
1.对_进行URL编码绕过
2.利用QUERY_STRING对于特殊字符的解析成"_"绕过
问题就出在第一个绕过上
但当我尝试构造payload=(1)http://lab1.xseclab.com/base13_ead1b12e47ec7cc5390303831b779d47/index.php?^%5f^=data:,(●’◡’●)’’'
发现在浏览器的搜索栏中竟然是:(2)http://lab1.xseclab.com/base13_ead1b12e47ec7cc5390303831b779d47/index.php?_=data:,(●’◡’●)
复制下来之后竟然是这样的:(3)http://lab1.xseclab.com/base13_ead1b12e47ec7cc5390303831b779d47/index.php?^%5f^=data:,(%E2%97%8F%27%E2%97%A1%27%E2%97%8F)
这就让我给搞糊涂了,这什么情况,对于大佬们来说简简单单,对于我来说十分的不解,于是我就去百度,谷歌,查到的信息加上我自己的推测,大致明白了这是怎么回事
URL编码
URL编码遵循下列规则: 每对name/value由&;符分开;每对来自表单的name/value由=符分开。如果用户没有输入值给这个name,那么这个name还是出现,只是无值。任何特殊的字符(就是那些不是简单的七位ASCII,如汉字)将以百分符%用十六进制编码,当然也包括象 =,&;,和 % 这些特殊的字符。其实url编码就是一个字符ascii码的十六进制。不过稍微有些变动,需要在前面加上“%”。比如“\”,它的ascii码是92,92的十六进制是5c,所以“\”的url编码就是%5c。
总结就是:URL编码会对除ASCLL以外的字符进行编码,使其变成%+16进制的模式,同时支持对ASCLL进行编码以满足特殊字符的要求例如"#"
那么为什么会出现(1),(2)两种情况?
原因就是浏览器的地址栏存在自动解码的功能,还是利用上面的代码做个试验
URL:http://localhost/test.%70hp?^%70^=%70
地址栏解析为:http://localhost/test.php?^p^=p
测试发现只要不输入一些特殊的字符的%+16进制,都能够被解析为ASCLL或者是对应的字符
因此地址栏实际上具有服务端一样的解析功能,但是无法对特殊字符或者说是限制对特殊字符的解析功能
那第三种情况是什么呢?再对众多的百度的URL进行测试后发现,这其实是原始的URL,没有经过任何的解析最原始的URL,服务端正是利用这个最原始的URL进行解析得出数据
URL编码绕过
由于URL支持ASCLL和%+16进制的模式,即使是最原始的URL也是由这两部分组成,同时浏览器不会对%+16进制的数据再进行编码,那么如果只是简单直接从url中获取数据,而对输入数据没有转码检查,例如限制"“符号的输入,由于”“可以是ASCLL码直接表示,直接对其匹配而没有对转码后的数据进行检查,那么我们对”“进行URL编码,在url解码后数据还原成”"从而绕过
小结:因此在对用户输入时,最好是对URL解码后的数据进行匹配,或者是双重匹配,避免URL编码绕过.
回到原题
对$SERVER[‘QUERY_STRING’]变量不熟悉的小伙伴,还是可以利用上面的代码进行测试
我们发现不管传入怎么样的格式都会以最原始的URL的数据格式输出,也就是说匹配的是ASCLL形式的"",那么我们对"“进行编码,就能绕过,同时服务器解析后还原为”",从而不影响其他匹配
关于QUERY STRING解析特殊字符成"_"
这个我也不知道没找到相关的资料(如果有链接的麻烦发我一下,谢谢),只好用BP跑一下了
貌似只有==[和.会被解析成"_"==
参考资料:
[1]:URL特殊字符以及解决办法:https://developer.aliyun.com/article/421813
[2]:ASCLL表:https://www.runoob.com/tags/html-ascii.html
*如若有不足之处,请指出,转载请表明出处
上一篇: BeautifulSoup使用相关知识
下一篇: week2实验