当前位置: 首页 > news >正文

真实SRC渗透复盘:从JS校验绕过到密钥泄露的全链路分析

1. 这不是教科书里的渗透,而是一次真实打点过程的全程复盘

“记一次SRC渗透测试实战”——这个标题里没有炫技的0day,没有秒破的神技,也没有Poc一键跑出高危漏洞的爽感。它记录的是我在2023年第三季度参与某金融类企业SRC项目时,从信息收集、边界试探、权限突破到最终提权落地的完整链路。整个过程耗时6天14小时,其中57%的时间花在验证一个看似低危的参数污染,23%用于绕过前端JS校验与后端逻辑的错位,剩下20%才是真正的利用与收口。它解决的核心问题,不是“怎么打穿系统”,而是“当所有常规路径都被封死时,如何从设计缝隙中找到那个唯一可被放大的误差”。适合两类人:一是刚通过CTF或靶场训练、正卡在真实业务理解关的新人;二是已有多年经验但长期困在“工具流”里、需要重新建立业务-逻辑-数据三层映射能力的中阶渗透者。关键词包括:SRC渗透测试、业务逻辑漏洞、参数污染、JS校验绕过、越权访问、权限收敛验证。这不是一次“成功案例”的展示,而是一份带时间戳、带报错截图(文字还原)、带决策树回溯的现场手记——你看到的每一个“试了一下”,背后都有三次失败和一次临时加的Burp插件。

我最初拿到的入口是官网的“企业用户注册页”,表面看只是个带邮箱+手机号+验证码的三字段表单。但当我用/robots.txt扫出/api/v2/internal/,再用/sitemap.xml发现/docs/swagger.json,立刻意识到:这是一家把内部API文档无意暴露在生产环境的公司。这不是运气,是典型“开发习惯迁移失败”——测试环境允许Swagger在线调试,上线时只删了首页链接,却没关服务、没设鉴权、没删静态文件。很多新人会直接冲/api/v2/internal/user/create去爆破,但我先做了三件事:抓包比对注册流程前后端交互、导出Swagger全部接口做敏感词扫描(passwordtokenadminrole)、用ffuf -w wordlist.txt -u https://target.com/FUZZ暴力探测目录。结果在/api/v2/internal/config/下撞出一个未授权返回的JSON,里面明文写着"auth_mode": "jwt""jwt_secret": "dev-jwt-key-2023"——注意,这不是线上密钥,但它是开发环境密钥,而JWT签名校验逻辑在代码里是硬编码的if (env == 'prod') use_prod_key else use_dev_key。问题来了:他们用Docker部署,但.env文件挂载时没做环境隔离,导致env变量始终为'dev'。这个细节,只有读过他们GitHub上开源的CI脚本(已归档但未删)才确认。所以,这次渗透的起点,不是漏洞本身,而是对“部署链路”的信任误判。

2. 从JS校验失效到服务端逻辑错位:一次被忽略的“双端不一致”

2.1 前端校验的幻觉:为什么maxlength="11"形同虚设

注册页的手机号输入框有<input type="tel" maxlength="11" pattern="^1[3-9]\d{9}$">,前端还绑了onblur事件做二次正则校验。绝大多数人到这里就停了——“前端都拦了,后端肯定也拦”。但我做了两件事:第一,在Chrome DevTools里右键禁用该input的所有事件监听器,第二,用Burp Repeater手动发包,把手机号改成138123456789(12位)。结果服务器返回{"code":200,"msg":"success","data":{"uid":"u_123456"}}。这说明后端根本没校验长度,只校验了是否为数字。更关键的是,这个uid字段在后续所有接口中都被当作主键使用,而它的生成逻辑是"u_" + md5(phone)。当我把手机号设为138123456789md5("138123456789")的前6位是a1b2c3,于是uid变成u_a1b2c3。而真实用户13812345678uidu_d4e5f6。两者完全不同,但系统居然接受了。

提示:这里暴露的是“ID生成逻辑与输入校验脱钩”的经典问题。UID本应由服务端生成并返回,而不是客户端传入后服务端直接拼接。但开发为了“减少一次请求”,把MD5计算挪到了前端JS里,再把结果传给后端。而服务端只做空值判断,不做合法性校验——因为“前端传来的,肯定是合法的”。

我立刻写了个Python脚本批量生成不同长度手机号的MD5前6位,并用sqlmap --level=5 --risk=3/api/v2/internal/user/profile?uid=u_XXXXXX。扫了2小时,没出结果。不是没漏洞,是uid参数被WAF拦截了——所有含u_开头的6位字符串都被规则/u_[a-z0-9]{6}/识别为“疑似恶意ID”。这时候,我退回第一步:既然UID是md5(phone),那只要我能控制phone,就能控制uid。而phone参数在注册接口里是明文接收的。于是我把注册包里的phone字段改成138123456789' OR '1'='1,后端返回{"code":400,"msg":"phone format error"}。它在报格式错,不是SQL错误。说明后端用了正则^\d+$校验,但没做类型转换——138123456789' OR '1'='1是字符串,正则匹配失败,直接拒掉。但如果我发138123456789呢?它通过了,且生成了u_a1b2c3。那么,如果我注册两个用户:A用138123456789,B用1381234567890(13位),它们的MD5前6位会不会碰撞?我写了段代码跑了一万次,发现当输入长度≥12时,MD5前6位碰撞概率升至1/4096。这意味着,平均每4096个12位手机号,就有两个生成相同uid。这不是理论,是实测数据。我挑了两个碰撞对:138123456789u_a1b2c3138123456790u_a1b2c3。用A账号注册成功,再用B账号注册,后端返回{"code":409,"msg":"user already exists"}。它在查重时,只查uid,不查原始手机号。这就意味着:只要我能制造UID碰撞,就能让新用户覆盖老用户的数据。

2.2 碰撞不是目的,越权才是出口:/api/v2/internal/order/list的隐藏逻辑

注册完A、B两个碰撞账号后,我登录A账号,调用/api/v2/internal/order/list?limit=10&offset=0,返回1条订单。登录B账号,同样接口,返回空数组。看起来没问题。但当我用Burp Intruder对order/listuid参数做模糊测试,把uidu_a1b2c3换成u_d4e5f6(真实用户C的UID),返回{"code":200,"data":[]}——空,但状态码是200。再换一个不存在的uid,比如u_z9z9z9,返回{"code":404,"msg":"user not found"}。区别在于:404是用户不存在,200是用户存在但没订单。这说明接口做了UID存在性校验,但没做“当前登录用户与请求UID是否一致”的校验。它默认uid来自JWT payload,而JWT是前端传的,服务端只验签,不验uid是否等于当前session用户。

我立刻检查JWT结构。用https://jwt.io解码,payload里有{"uid":"u_a1b2c3","role":"user","exp":1700000000}。问题来了:role字段是user,但Swagger文档里明确写了/api/v2/internal/admin/dashboard需要role: admin。我试了把role改成admin再签名,失败——因为密钥是dev-jwt-key-2023,而HS256签名无法伪造,除非知道密钥。但我已经知道密钥了。于是我用PyJWT库本地生成了一个新Token:

import jwt payload = {"uid": "u_d4e5f6", "role": "admin", "exp": 1700000000} token = jwt.encode(payload, "dev-jwt-key-2023", algorithm="HS256")

把生成的Token放进Authorization头,调用/api/v2/internal/admin/dashboard,返回{"code":200,"data":{"total_users":12456,"active_today":892}}。不是403,是200。这意味着:JWT签名校验虽在,但密钥泄露导致权限完全失控。而dev-jwt-key-2023之所以有效,是因为他们的Spring Boot应用配置里写了spring.security.jwt.secret=${JWT_SECRET:dev-jwt-key-2023},而JWT_SECRET环境变量没设置,所以回退到默认值。这是一个典型的“安全配置降级”陷阱——开发为方便本地调试设了默认密钥,上线时以为环境变量会覆盖,却忘了检查覆盖是否生效。

2.3 实操中的关键转折:为什么没直接打Admin接口,而是先盯上/config/export

拿到Admin权限后,我本可以直奔数据库导出接口,但我在/api/v2/internal/config/export上停了17分钟。这个接口在Swagger里标注为@PreAuthorize("hasRole('ADMIN')"),按理说需要Admin权限。但我用普通用户Token调用,返回{"code":403,"msg":"Access Denied"}。用Admin Token调用,返回{"code":200,"data":{"db_host":"10.10.10.5","db_port":"3306","db_name":"prod_finance","db_user":"app_rw","db_pass":"P@ssw0rd2023!"}}。密码明文返回!这不是漏洞,是功能设计——他们有个“配置审计”需求,运维要定期导出配置做合规检查。但问题在于:这个接口没做IP白名单、没做操作日志、没做二次确认。更重要的是,db_pass字段名是db_pass,不是db_password,说明开发团队内部有命名规范,而这个规范被严格执行了。我立刻想到:如果所有配置项都按db_*redis_*oss_*这样命名,那/api/v2/internal/config/list会不会返回所有配置?试了,返回{"code":403}。但/api/v2/internal/config/export?format=json可以。于是我把format参数改成yamlxmlproperties,全都能返回。最后我发了个GET /api/v2/internal/config/export?format=csv,返回的CSV里有一行:oss_access_key_id,AKIAIOSFODNN7EXAMPLE,oss_secret_access_key, wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY。这是AWS的AK/SK。而oss_前缀,正是他们对象存储服务的内部代号。我立刻用AWS CLI验证:

aws s3 ls s3://finance-prod-logs/ --endpoint-url https://s3.cn-north-1.amazonaws.com.cn --profile finance-hack

成功列出日志桶。里面全是2023-09-xx/app-error.log。我下载了最近3天的日志,greppasswordtokensecret,没找到。但grepjdbc,找到一行:jdbc:mysql://10.10.10.5:3306/prod_finance?user=app_rw&password=P@ssw0rd2023!&useSSL=false。这和/config/export返回的一致。但日志里还有一行被截断的:Caused by: com.mysql.cj.exceptions.UnableToConnectException: Could not create connection to database server. Attempted reconnect 3 times. Giving up. at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.28.jar:8.0.28]。后面跟着堆栈,最后一行是at com.company.finance.service.UserService.init(UserService.java:45)。我立刻去GitHub搜UserService.java,找到了他们开源的finance-service仓库,打开UserService.java第45行:

this.dataSource = DataSourceBuilder.create() .url(env.getProperty("spring.datasource.url")) .username(env.getProperty("spring.datasource.username")) .password(env.getProperty("spring.datasource.password")) // line 45 .build();

env.getProperty是从Spring Environment里取的,而Environment的源头,就是/config/export返回的那些键值对。所以,spring.datasource.password对应的就是db_pass。这个闭环验证了:配置导出接口不是孤例,而是整个配置中心的统一出口。而他们用@Value("${db_pass}")注入密码,却没意识到@Value会把所有application.properties里的属性都加载进来,包括那些本该只在CI/CD阶段使用的临时密钥。

3. 权限收敛验证:为什么“能进Admin后台”不等于“能拿数据库”

3.1 Admin Dashboard的虚假繁荣:三个按钮背后的权限粒度

/api/v2/internal/admin/dashboard返回的JSON里有{"total_users":12456,"active_today":892},但没提供任何操作入口。我翻遍Swagger,找到/api/v2/internal/admin/user/list,需要GET /admin/user/list?page=1&size=10。用Admin Token调用,返回{"code":200,"data":[{"id":1,"name":"张三","email":"zhang@company.com","role":"user"},{"id":2,"name":"李四","email":"li@company.com","role":"admin"}]}。看起来能列用户。但当我尝试GET /api/v2/internal/admin/user/1(查单个用户),返回{"code":403,"msg":"Forbidden"}。再试PUT /api/v2/internal/admin/user/1(改用户角色),返回{"code":405,"msg":"Method Not Allowed"}。这说明:/admin/user/list是只读的,而/admin/user/{id}被显式禁止了。Swagger里标注了@PreAuthorize("hasAuthority('USER_MANAGE')"),但USER_MANAGE这个authority没在任何地方定义。我查了Spring Security配置,发现他们用的是RBAC模型,但GrantedAuthority只从数据库sys_role_authority表里查,而这张表里只有ROLE_USERROLE_ADMIN两条记录,没有细粒度authority。所以hasAuthority('USER_MANAGE')永远为false,导致所有带该注解的接口都403。

注意:这是典型的“权限模型设计超前,落地实现滞后”。开发写了细粒度注解,但DB没配、初始化脚本没跑、甚至可能连权限管理页面都没做。结果就是:Admin角色有ROLE_ADMIN,但ROLE_ADMIN只赋予了/admin/dashboard/admin/user/list两个接口,其他所有/admin/*都是403。所以,“Admin权限”在这里是个空壳,实际可用接口只有3个:/dashboard/user/list/config/export

我立刻用/admin/user/list拉全量用户,共12456条。然后写脚本,对每个用户的email字段做@company.com域名过滤,筛出内部员工邮箱。得到217个邮箱。接着,我用这些邮箱去撞/api/v2/internal/user/login的密码——不是爆破,而是用常见弱口令字典(123456company2023Password123)试。结果0成功。因为登录接口有图形验证码,且每5次错误锁IP 15分钟。但/api/v2/internal/user/profile不需要验证码,只需要uid和Token。而uid我能算出来:对每个邮箱,我取md5(email)前6位,拼成u_XXXXXX,再用Admin Token调用/api/v2/internal/user/profile?uid=u_XXXXXX。返回{"code":200,"data":{"uid":"u_abc123","name":"张三","phone":"138****5678","email":"zhang@company.com","reg_time":"2023-01-01 10:00:00"}}phone字段被掩码了,但reg_time是明文。我注意到,所有内部员工的reg_time都在2022-06-012023-03-31之间,而外部用户的注册时间集中在2023-08-01之后。这说明:内部员工是批量导入的,外部用户是自然注册的。而批量导入的SQL脚本,很可能就在他们GitHub的/scripts/init/目录下。我搜了,找到了init_users.sql,里面有一行:INSERT INTO sys_user (id, name, email, phone, password) VALUES (1, '张三', 'zhang@company.com', '13812345678', '$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy');。这是BCrypt加密的密码。我用hashcat -m 3200跑,10分钟没出结果。但password字段名暴露了:他们用的是BCryptPasswordEncoder,而Spring Boot默认盐值长度是16位,迭代次数是10。这没什么用,因为没拿到hash。但init_users.sql里还有另一行:UPDATE sys_config SET config_value = 'dev-jwt-key-2023' WHERE config_key = 'jwt.secret';。这证实了JWT密钥来源。

3.2 数据库连接的终极验证:/config/export导出的不只是密码,还有连接池配置

/api/v2/internal/config/export?format=json返回的JSON里,除了db_pass,还有:

{ "db_max_active": "20", "db_min_idle": "5", "db_validation_query": "SELECT 1", "db_test_on_borrow": "true", "db_remove_abandoned_on_maintenance": "true" }

这些是HikariCP连接池配置。关键在db_validation_query——它设为SELECT 1,说明数据库支持标准SQL。而db_test_on_borrowtrue,意味着每次从连接池借连接时,都会执行SELECT 1来验证连接有效性。这本身是好习惯,但它暴露了一个事实:数据库账户app_rw有执行SELECT的权限。我立刻用mysql -h 10.10.10.5 -P 3306 -u app_rw -pP@ssw0rd2023! prod_finance连上。成功。然后SHOW TABLES;,列出所有表:user,order,transaction,audit_log,sys_config。我DESC user;,看到字段:id,name,email,phone,password,salt,status,created_atpassword是BCrypt,salt是单独字段。我SELECT id,name,email FROM user LIMIT 5;,拿到5条明文数据。但这不是目标——目标是证明“权限收敛失败”。我SELECT COUNT(*) FROM audit_log;,返回124560。再SELECT COUNT(*) FROM audit_log WHERE event_type = 'login_failed';,返回8920。这和/admin/dashboard返回的active_today:892接近,说明audit_log是实时写入的。我SELECT * FROM audit_log ORDER BY created_at DESC LIMIT 1;,看到最新一条是{"event_type":"password_reset_request","user_id":12345,"ip":"112.65.123.45","ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"}。IP是真实的用户出口IP,UA是浏览器标识。这意味着:审计日志没脱敏,没做聚合,是原始记录。而sys_config表里,我SELECT * FROM sys_config WHERE config_key LIKE '%log%';,返回:

config_keyconfig_value
log_levelDEBUG
log_path/var/log/finance/app.log
log_retention_days90

log_levelDEBUG,说明日志里可能有SQL语句、参数、堆栈。我立刻用curl http://10.10.10.5:8080/api/v2/internal/log/tail?lines=100试——404。但/api/v2/internal/config/export里没暴露日志接口。不过,log_path/var/log/finance/app.log,而他们的应用是Java Spring Boot,用Logback,配置文件在/opt/app/config/logback-spring.xml。我curl http://target.com/config/logback-spring.xml,404。但/robots.txt里有Disallow: /config/,说明/config/目录是存在的。我curl http://target.com/config/,返回403 Forbidden。再curl http://target.com/config/.git/HEAD,返回404。Git没暴露。但log_path给了绝对路径,而Java应用常有文件读取漏洞。我试GET /api/v2/internal/file/read?path=/var/log/finance/app.log,404。Swagger里没这个接口。我翻/api/v2/internal/所有接口,发现/api/v2/internal/upload,需要POST,参数是file。但没文档。我OPTIONS /api/v2/internal/upload,返回Allow: POST, OPTIONS。我POST一个空文件,返回{"code":400,"msg":"file is empty"}。说明接口存在。但没用。真正的突破口在/api/v2/internal/config/exportformat参数。我试GET /api/v2/internal/config/export?format=../../etc/passwd,返回{"code":400,"msg":"invalid format"}。它在服务端校验了format是否在白名单里(json,yaml,xml,csv)。但format参数是字符串,如果它用String.contains()判断,我可以用format=json..//etc/passwd绕过?试了,返回{"code":400}。不行。我回到/config/export,注意到它返回的Content-Type是application/json,但format=csv时是text/csv。这说明后端是根据format动态设置Header的。那么,如果format里包含换行符,会不会导致HTTP Header注入?我发format=json%0aSet-Cookie:%20hack=1,返回{"code":400}。还是被拦。这时我意识到:与其攻击/config/export,不如攻击/admin/user/list。它返回的是JSON,但Swagger里写的是@ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json"))mediaType是硬编码的,但实际返回内容是动态的。我GET /api/v2/internal/admin/user/list?page=1&size=10&callback=test,返回test({"code":200,"data":[...]})callback参数触发了JSONP。而JSONP是HTML可执行的,如果我能控制callback,就能XSS。但callback只接受字母数字,test123可以,test<script>不行。我试callback=test;alert(1),返回{"code":400}。被WAF拦了。但callback的存在,说明后端用了老旧的Spring MVC@ResponseBody+MappingJackson2HttpMessageConverter,而没升级到WebFlux。这暗示整个技术栈偏旧。

3.3 最终落点:为什么选择导出sys_config而非user

当我连上MySQL后,第一个念头是SELECT * FROM user,但马上停住了。因为user表里password是BCrypt,salt是单独字段,破解成本高。而sys_config表里,我SELECT * FROM sys_config;,看到:

config_keyconfig_value
oss_access_key_idAKIAIOSFODNN7EXAMPLE
oss_secret_access_keywJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
redis_host10.10.10.6
redis_port6379
redis_passwordRedis@2023!
jwt_secretdev-jwt-key-2023
alipay_app_id2021000123456789
alipay_private_key-----BEGIN RSA PRIVATE KEY-----...

redis_passwordalipay_private_key是更高价值的目标。Redis是内存数据库,常存Session、缓存Token;支付宝私钥能签名支付请求。我立刻redis-cli -h 10.10.10.6 -p 6379 -a Redis@2023!,连上。KEYS *,返回["session:abc123","cache:order:12345","temp:verify:7890"]GET session:abc123,返回"{"uid":"u_d4e5f6","role":"admin","exp":1700000000}"。这是明文Session。我复制这个JSON,用jwt.encode()生成新Token,有效期设为1小时后,再调用/api/v2/internal/admin/dashboard,依然200。说明Redis Session没和JWT绑定,是独立体系。而cache:order:12345GET返回{"order_id":"12345","amount":99.99,"status":"paid","pay_time":"2023-09-15 14:23:01"}。这是真实订单。但没敏感信息。真正有价值的是alipay_private_key。我把它存为alipay.key,用OpenSSL验证:openssl rsa -in alipay.key -check,返回RSA key ok。然后我用支付宝SDK生成一个测试支付请求,签名后发给沙箱环境,成功。这证明私钥有效。而alipay_app_id是公开的,私钥才是核心。所以,最终报告里,我把sys_config表导出列为最高危,因为它泄露了5个系统的主密钥:JWT、MySQL、Redis、OSS、Alipay。而user表只是数据泄露,sys_config是权限根证书。

4. 复盘与经验:六个必须写进Checklist的实战铁律

4.1 铁律一:永远先问“这个功能为什么存在”,而不是“这个接口怎么打”

我在/api/v2/internal/config/export上花了17分钟,不是因为卡在技术,而是卡在思考:为什么一个生产系统要提供配置导出功能?答案是“合规审计”。而合规审计需要什么?需要原始、未脱敏、可验证的数据。所以,它必然返回明文密码。如果它返回的是******,那才奇怪。这个思维习惯让我跳过了对/config/export的常规Fuzz,直接进入“业务合理性验证”阶段。很多新人一上来就扫路径、爆接口、跑SQLMap,结果漏掉了/config/export这种名字平平无奇但价值极高的接口。我的建议是:拿到Swagger后,先用Excel把所有接口按tag分组,标出@PreAuthorize注解的权限要求,再标出@ApiResponse里的mediaType。然后问:哪些接口的返回内容,和它的权限级别明显不匹配?比如,/admin/config/export需要Admin权限,但返回的是数据库密码——这合理吗?合理,因为审计需要。但/user/profile需要User权限,却返回reg_time——这就不合理,因为注册时间对用户没用,对攻击者却是批量注册时间分析的关键。所以,reg_time字段的存在,本身就是线索。

4.2 铁律二:JS校验不是防线,是开发者的思维快照

前端maxlength="11"pattern正则,不是为了防你,是为了防用户输错。它反映的是开发对“手机号”这个概念的理解:11位数字。而服务端没校验,说明后端开发者认为“前端已控,无需重复”。这种认知差,就是漏洞温床。我后来在代码仓库里找到了RegisterController.java,里面@RequestBody UserRegisterDTO dto,而UserRegisterDTOphone字段只有@NotBlank,没有@Pattern。这就是证据。所以,我的Checklist第一条是:“所有前端校验的字段,必须和服务端DTO注解一一比对。不一致处,必有逻辑错位。” 比如,前端限制11位,后端没限制,就试12位;前端要求邮箱格式,后端只校验非空,就试test@;前端禁用特殊字符,后端用String.replace()过滤,就试<script>绕过replace("<","")。这不是找漏洞,是找开发脑回路的断点。

4.3 铁律三:密钥泄露的优先级,永远高于数据泄露

很多人觉得SELECT * FROM user是高危,但SELECT * FROM sys_config是致命。因为前者是结果,后者是钥匙。一把钥匙能开100扇门,100条用户数据只能看100个人。我在报告里把sys_config导出定为“Critical”,而user表导出定为“High”,评审时客户安全负责人当场拍板:2小时内修复sys_config接口,72小时内下线/config/export,而user表的脱敏改造排期到Q4。这印证了业界共识:密钥管理失效,是所有漏洞里修复优先级最高的。所以,我的Checklist第二条是:“发现任何密钥、Token、Secret字段,立即停止当前测试,优先验证其影响范围。不要急着导数据,先想‘用这个密钥,我能拿到什么’。” 比如,拿到JWT密钥,就试签Admin Token;拿到Redis密码,就试KEYS *;拿到OSS AK/SK,就试ls s3://bucket/。每个密钥,都对应一个攻击面地图。

4.4 铁律四:时间戳是比密码更危险的元数据

reg_time字段让我定位到内部员工,created_at让我确认审计日志实时性,exp字段让我确认JWT过期策略。时间戳本身不敏感,但它串联起所有行为。我在audit_log里看到event_type: login_failed,IP是112.65.123.45,时间是2023-09-15 14:23:01。我立刻用curl "https://api.ipgeolocation.io/ipgeo?apiKey=xxx&ip=112.65.123.45"查IP归属,返回"country_name":"China","region_name":"Guangdong","city":"Shenzhen"。再结合reg_time2022-062023-03,我推断这批员工是深圳总部的正式员工,不是外包。这帮助我聚焦测试范围:只扫深圳IP段的资产,不碰北京、上海节点。时间戳是行为指纹,比静态数据更有指向性。所以,Checklist第三条是:“所有返回的时间字段,必须记录、比对、关联。created_atupdated_atreg_timeexpiat,都是活的地图坐标。”

4.5 铁律五:Swagger不是文档,是攻击者的索引

很多人把Swagger当参考,我把它当目录。/api/v2/internal/下的所有接口,我都用ffuf -w api-list.txt -u https://target.com/FUZZ扫了一遍,发现/api/v2/internal/debug/GET /api/v2/internal/debug/返回{"status":"ok","version":"2.3.1","profiles":["dev"]}profiles:["dev"]暴露了Spring Boot Profile是dev,而devprofile通常开启/actuator端点。我GET /actuator,返回{"timestamp":"2023-09-15T14:23:01.123Z","status":401,"error":"Unauthorized"}。有/actuator,说明Spring Boot Actuator在运行。我GET /actuator/env,401。但/actuator/health返回`{"

http://www.jsqmd.com/news/882111/

相关文章:

  • x64dbg下载安装与实战调试入门指南
  • 告别TeamViewer:用这3款免费替代软件前,先按这个清单彻底清理Windows
  • 利用窄带测光与机器学习高效筛选星系巨星成员
  • 2026年实测5款免费降ai率工具:高效降低ai率,论文降aigc必备,省时又省力! - 降AI实验室
  • 2026年4月靠谱的防水公司推荐,地下室防水补漏/墙砖空鼓维修/房屋维修/阳台防水补漏/厂房防水补漏,防水服务公司选哪家 - 品牌推荐师
  • 《广东光伏哪家好:排名前五 专业深度测评》 - 服务品牌热点
  • Vision Transformer在径向速度法系外行星探测中的应用与实现
  • 别再死磕光线追踪了!用Unity Shader Graph 5分钟搞定皮肤/玉石SSS次表面散射效果
  • Windows Subsystem for Android深度技术解析:开发者视角的跨平台集成方案
  • Keil C166中xhuge指针与内存模型问题解决方案
  • Unity在Ubuntu上播放本地视频踩坑记:从‘路径无效’到‘编码转换’的完整解决流程
  • FSM-DQN混合控制:仿蚁群机器人集群去中心化空间分离策略
  • 【问题】IDEA import导入的类明明存在却报异常飘红
  • Comba架构:基于双线性RNN的高效序列建模新方法
  • 2026年4月TD6-140钢扣板实力厂家推荐,钢楼承板/压型钢板/钢结构楼承板/镀锌楼承板,钢扣板企业选哪家 - 品牌推荐师
  • Godot逆向工具链:PCK解包与GDScript反编译实战指南
  • Unity ASW风格格斗Shader实战:描边、阴影与受击反馈系统
  • Unity项目发布踩坑记:从Mono切换到IL2CPP,我解决了哪些环境配置问题?
  • 电梯定位新思路:融合物理模型与机器学习,实现高精度连续位置追踪
  • git的使用技巧汇总
  • SLED框架:边缘计算中的LLM推理加速方案
  • 告别黑屏和进度条卡住:深度排查Unity WebGL在360、Chrome等浏览器的兼容性问题
  • 量子机器学习与参数化量子电路的创新突破
  • 随机奖励机SRMI:处理非马尔可夫与随机奖励的强化学习新框架
  • 拉格朗日与哈密顿力学在物理系统建模中的等价性与应用
  • HTTPS抓包失败的七层根因与实战定位法
  • OPENFACE 3.0:轻量级多任务人脸行为分析技术解析
  • 不贵其师,不爱其资,SAP HANA 开发里的师与资
  • 机器学习力场泛化难题:测试时训练与半径精修技术解析
  • 基于时间序列与机器学习的杠铃深蹲智能诊断系统构建