diff --git a/SUMMARY.md b/SUMMARY.md index d88973f..dd00885 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -65,7 +65,9 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [3.3.5 Linux 堆利用(上)](doc/3.3.5_heap_exploit_1.md) * [3.3.6 Linux 堆利用(中)](doc/3.3.6_heap_exploit_2.md) * [3.3.7 Linux 堆利用(下)](doc/3.3.7_heap_exploit_3.md) - * [3.3.8 Windows 内核漏洞利用](doc/3.3.8_windows_kernel_exploit.md) + * [3.3.8 内核 ROP](doc/3.3.8_kernel_rop.md) + * [3.3.9 Linux 内核漏洞利用](doc/3.3.9_linux_kernel_exploit.md) + * [3.3.10 Windows 内核漏洞利用](doc/3.3.10_windows_kernel_exploit.md) * [3.4 Web](doc/3.4_web.md) * [3.4.1 SQL 注入利用](doc/3.4.1_sql_injection.md) * [3.4.2 XSS 漏洞利用](doc/3.4.2_xss.md) diff --git a/doc/1.4.1_html_basic.md b/doc/1.4.1_html_basic.md index e1794e0..1da61d2 100644 --- a/doc/1.4.1_html_basic.md +++ b/doc/1.4.1_html_basic.md @@ -5,8 +5,8 @@ - [HTML 编码](#html-编码) - [HTML5 新特性](#html5-新特性) -## 什么是 HTML +## 什么是 HTML HTML 是用来描述网页的一种语言。 - HTML 指的是超文本标记语言 (Hyper Text Markup Language) @@ -20,13 +20,13 @@ HTML 是用来描述网页的一种语言。 由于是通过浏览器动态解析,因此可以使用普通文本编辑器来编写 HTML。 -## HTML 中的标签与元素 +## HTML 中的标签与元素 标签和元素共同构成了 HTML 多样的格式和丰富的功能。 HTML 元素以开始标签起始,以结束标签终止。元素处于开始标签与结束标签之间,标签之间可以嵌套,一个典型的 HTML 文档如下: -``` +```html @@ -38,11 +38,9 @@ HTML 元素以开始标签起始,以结束标签终止。元素处于开始标 ``` #### 信息隐藏 - HTML 中的部分标签用于元信息展示、注释等功能,并不用于内容的显示。另一方面,一些属性具有修改浏览器显示样式的功能,在 CTF 中常被用来进行信息隐藏。 - -``` +```html 标签 ,定义注释 ,定义文档类型 @@ -55,11 +53,9 @@ hidden,隐藏元素 ``` #### XSS - 关于 XSS 漏洞的详细介绍见 1.4.5 节的 OWASP Top Ten Project 漏洞基础。导致 XSS 漏洞的原因是嵌入在 HTML 中的其它动态语言,但是 HTML 为恶意注入提供了输入口。 常见与 XSS 相关的标签或属性如下: - ``` @@ -43,40 +42,34 @@ Opera|Carakan| ``` + ## JavaScript 数据类型 - 作为弱类型的语言,JS 的变量声明不需要指定数据类型: - -``` +```js var pi=3.14; var pi='ratio of the circumference of a circle to its diameter'; ``` -当然,可以通过“ new ”来声明变量类型 - -``` +当然,可以通过“ new ”来声明变量类型: +```js var pi=new String; var pi=new Number; var pi=new Boolean; var pi=new Array; var pi=new Object; ``` - 上一个示例也展示了 JS 的数据类型,分别是字符串、数字、布尔值、数组和对象。 有两个特殊的类型是 Undefined 和 Null,形象一点区分,前者表示有坑在但坑中没有值,后者表示没有坑。另外,所有 JS 变量都是对象,**但是需要注意的是,对象声明的字符串和直接赋值的字符串并不严格相等**。 + ## JavaScript 编程逻辑 - -### 基础 - +#### 基础 JS 语句使用分号分隔。 ### 逻辑语句 - -if 条件语句 - -``` +if 条件语句: +```js if (condition) { 代码块 @@ -87,9 +80,8 @@ else } ``` -switch 条件语句 - -``` +switch 条件语句: +```js switch(n) { case 1: @@ -103,32 +95,28 @@ switch(n) } ``` -for/for in 循环语句 - -``` +for/for in 循环语句: +```js for (代码1;代码2;代码3) { 代码块 } ``` - -``` +```js for (x in xs) { 代码块 } ``` -while/do while 循环语句 - -``` +while/do while 循环语句: +```js while (条件) { 代码块 } ``` - -``` +```js do { 代码块 @@ -136,50 +124,43 @@ do while (条件); ``` -## JavaScript 打印数据 +## JavaScript 打印数据 在浏览器中调试代码时,经常用到的手段是打印变量。 - -函数|作用 ---|-- -window.alert()|弹出警告框 -document.write()|写入HTML文档 -console.log()|写入浏览器控制台 +| 函数 | 作用 | +| --- | --- | +| window.alert() | 弹出警告框 | +| document.write() | 写入HTML文档 | +| console.log() | 写入浏览器控制台 | ![](../pic/1.4.3_window_alert.png) +![](../pic/1.4.3_document_write.png) -![](../pic/1.4.3_document_write) ## JavaScript 框架 - JS 同样有许多功能强大的框架。大多数的前端 JS 框架使用外部引用的方式将 JS 文件引入到正在编写的文档中。 -### jQuery - +#### jQuery jQuery 封装了常用的 JS 功能,通过选择器的机制来操纵 DOM 节点,完成复杂的前端效果展示。 -### Angular - +#### Angular 实现了前端的 MVC 架构,通过动态数据绑定来简化数据转递流程。 -### React - +#### React 利用组件来构建前端UI的框架 -### Vue - +#### Vue MVVM 构架的前端库,理论上讲,将它定义为数据驱动、组件化的框架,但这些概念也可能适用于其他框架,所以可能只有去真正使用到所有框架才能领悟到它们之间的区别。 -### 其他 - +#### 其他 还有许许多多针对不同功能的框架,比如针对图表可视化、网络信息传递或者移动端优化等等。 -### 双向数据绑定 - +#### 双向数据绑定 传统基于MVC的架构的思想是数据单向的传送到 View 视图中进行显示,但是有时我们还需要将视图层的数据传输回模型层,这部分的功能就由前端 JS 来接手,因此许多近几年出现的新框架都使用数据双向绑定来完成MVVM的新构架,这就带给了用户更多的权限接触到程序的编程逻辑,进而产生一些安全问题,比较典型的就是许多框架曾经存在的模板注入问题。 + ## JavaScript DOM 和 BOM | | | @@ -187,93 +168,57 @@ MVVM 构架的前端库,理论上讲,将它定义为数据驱动、组件化 |DOM|文档对象模型,JS 通过操纵 DOM 可以动态获取、修改 HTML 中的元素、属性、CSS 样式,这种修改有时会带来 XSS 攻击风险| |BOM|浏览器对象模型,类比于 DOM,赋予 JS 对浏览器本身进行有限的操纵,获取 Cookie、地理位置、系统硬件或浏览器插件信息等| -## JavaScript 混淆 +## JavaScript 混淆 由于前端代码的可见性,出于知识产权或者其他目的,JS 代码通过混淆的方法使得自己既能被浏览器执行,又难以被人为解读。常见的混淆方法有重命名变量名和函数名、挤压代码、拼接字符、使用动态执行函数在函数与字符串之间进行替换等。下面对比代码混淆前后的差异。 -``` -混淆前 +混淆前: +```js console.log('Hello World!'); - -混淆后 +``` +混淆后: +```js console["\x6c\x6f\x67"]('\x48\x65\x6c\x6c\x6f \x57\x6f\x72\x6c\x64\x21'); - -更加复杂的混淆 +``` +更加复杂的混淆后: +```js eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p;}('1.0(\'3 2!\');',4,4,'log|console|World|Hello'.split('|'),0,{})) ``` 由于之前提到的特性,无论混淆有多么复杂,最终它都能够在浏览器中被解释执行。 -## 使用 Node.js 执行后端 JavaScript +## 使用 Node.js 执行后端 JavaScript 在 [安装完成](https://nodejs.org/en/download/) Node.js 后,我们可以尝试编写第一个后端 JS 程序。 1.打开文本编辑器,写入 - -``` +```js console.log("Hello World"); ``` - -并保存为 hello.js +并保存为 `hello.js` 2.使用 - -``` +```js node hello.js ``` - -来执行文件 +来执行文件。 ![](../pic/1.4.3_nodejs) + ## Node.js 模块 - Node.js 同样通过丰富的模块提供强大的功能,模块使用 npm 进行管理。 - -events,事件模块,提供事件触发和事件监听功能 - -util,核心功能模块,用于弥补核心 JS 功能的不足 - -fs,文件操作模块,提供文件操作 API - -http,Web协议模块,提供 Web 协议交互功能 - -express,Web框架,用于快速构建 Web 应用服务 - -vm,沙箱模块,提供干净的上下文环境 +- `events`:事件模块,提供事件触发和事件监听功能 +- `util`:核心功能模块,用于弥补核心 JS 功能的不足 +- `fs`:文件操作模块,提供文件操作 API +- `http`:Web 协议模块,提供 Web 协议交互功能 +- `express`:Web 框架,用于快速构建 Web 应用服务 +- `vm`:沙箱模块,提供干净的上下文环境 后端 JS 就会存在其他语言后端所同样存在安全问题,包括基础的 Web 攻击、服务端模板注入、沙箱逃逸、内存溢出等问题。 + ## 参考资料 - -[JavaScript 教程](http://www.runoob.com/js/js-tutorial.html) - -[Node.js 教程](http://www.runoob.com/nodejs/nodejs-tutorial.html) - -[浅谈 Node.js 安全](https://zhuanlan.zhihu.com/p/28105239) - - - - - - - - - - - - - - - - - - - - - - - - - - +- [JavaScript 教程](http://www.runoob.com/js/js-tutorial.html) +- [Node.js 教程](http://www.runoob.com/nodejs/nodejs-tutorial.html) +- [浅谈 Node.js 安全](https://zhuanlan.zhihu.com/p/28105239) diff --git a/doc/1.4.4_webserver_basic.md b/doc/1.4.4_webserver_basic.md index 6290416..a8bc76a 100644 --- a/doc/1.4.4_webserver_basic.md +++ b/doc/1.4.4_webserver_basic.md @@ -5,64 +5,56 @@ - [IIS](#iis) - [如何获取 Web 服务指纹](#如何获取-web-服务指纹) + 由于涉及到 Web 服务器和应用服务器的差别问题,这里着重介绍三款使用广泛的 Web 服务器。 当客户端按照 HTTP 协议发送了请求,服务端也写好了处理请求的逻辑代码,这时就需要一个中间人来接收请求,解析请求,并将请求放入后端代码中执行,最终将执行结果返回的页面传递给客户端。另外,我们还要保证整个服务能同时被大规模的人群使用,Web 服务器就充当了这样的角色。 -## Apache HTTP Server +## Apache HTTP Server Apache HTTP Server 以稳定、安全以及对 PHP 的高效支持而被广泛用于 PHP 语言中,WAMP 或者 LAMP 就是它们组合的简称,即 Windows 或者 Linux 下的 Apache2+Mysql+PHP。 #### 安装 - -Windows 下推荐直接[安装](http://www.wampserver.com/en/) WAMP 环境 +Windows 下推荐直接[安装](http://www.wampserver.com/en/) WAMP 环境。 Ubuntu 下可以依次使用命令安装,需要注意的是不同的系统版本对 PHP 的支持情况不同,这里以 ubuntu 16.04 为例。 - ``` -sudo apt-get install apache2 -sudo apt-get install mysql-server mysql-client -sudo apt-get install php7.0 -sudo apt-get install libapache2-mod-php7.0 -sudo apt-get install php7.0-mysql -service apache2 restart -service mysql restart +$ sudo apt-get install apache2 +$ sudo apt-get install mysql-server mysql-client +$ sudo apt-get install php7.0 +$ sudo apt-get install libapache2-mod-php7.0 +$ sudo apt-get install php7.0-mysql +$ service apache2 restart +$ service mysql restart ``` #### 组件 - Apache 服务器拥有强大的组件系统,这些组件补充了包括认证、日志记录、命令交互、语言支持等复杂功能,同样在 Apache 的发展过程中,许多组件都出现过漏洞,包括资源溢出、拒绝服务、远程命令执行等。 关于 Apache 的组件历史漏洞可以在 [https://www.exploit-db.com](https://www.exploit-db.com) 中进行查看 #### 文件后缀解析特性 - Apache 支持多后缀解析,对文件的后缀解析采用从右向左的顺序,如果遇到无法识别的后缀名就会依次遍历剩下的后缀名。 -同时,还可以在配置文件如下选项中增加其他后缀名 - +同时,还可以在配置文件如下选项中增加其他后缀名: ``` ``` -更多的后缀名支持可以查看 mime.type 文件 - +更多的后缀名支持可以查看 `mime.type` 文件。 ## Nginx - Nginx 的特点在于它的负载均衡和反向代理功能,在访问规模庞大的站点上通常使用 Nginx 作为服务器。同样,Nginx 也和 Mysql、PHP 一同构成了 WNMP 和 LNMP 环境。和 Apache 默认将 PHP 作为模块加载不同的是,Nginx 通过 CGI 来调用 PHP。 #### 安装 +Windows 由于没有官方网站的 WNMP,大家可以选择 Github 上的 WNMP 项目或者其他用户打包好的安装环境进行安装。 -Windows,由于没有官方网站的 WNMP,大家可以选择 Github 上的 WNMP 项目或者其他用户打包好的安装环境进行安装 - -Ubuntu,这里以 FPM 配置为例 - +Ubuntu 这里以 FPM 配置为例: ``` -sudo apt-get install nginx -sudo apt-get install php7.0 -sudo apt-get install php7.0-fpm +$ sudo apt-get install nginx +$ sudo apt-get install php7.0 +$ sudo apt-get install php7.0-fpm 打开 vim /etc/nginx/sites-available/default 修改配置 server { @@ -76,27 +68,23 @@ server { ...... ...... } -service nginx restart -sudo apt-get install mysql-server php7.0-mysql -sudo apt-get install mysql-client +$ service nginx restart +$ sudo apt-get install mysql-server php7.0-mysql +$ sudo apt-get install mysql-client ``` #### 文件后缀解析特性 - -由于 Nginx 对 CGI 的使用更加广泛,所以 PHP 在 CGI 的一些解析特性放到 Nginx 这里来讲解,PHP 具有对文件路径进行修正的特性,使用如下配置参数 - +由于 Nginx 对 CGI 的使用更加广泛,所以 PHP 在 CGI 的一些解析特性放到 Nginx 这里来讲解,PHP 具有对文件路径进行修正的特性,使用如下配置参数: ``` cgi.fix_pathinfo = 1 ``` 当使用如下的 URL 来访问一个存在的 1.jpg 资源时,Nginx 认为这是一个 PHP 资源,于是会将该资源交给 PHP 来处理,而 PHP 此时会发现 1.php 不存在,通过修正路径,PHP 会将存在的 1.jpg 作为 PHP 来执行。 - ``` http://xxx/xxx/1.jpg/1.php ``` 相似的绕过方式还有以下几种方式: - ``` http://xxx/xxx/1.jpg%00.php http://xxx/xxx/1.jpg \0.php @@ -106,18 +94,14 @@ http://xxx/xxx/1.jpg \0.php ## IIS - IIS 被广泛内置于 Windows 的多个操作系统中,只需要在控制面板中的 Windows 服务下打开 IIS 服务,即可进行配置操作。作为微软的 Web 服务器,它对 .net 的程序应用支持最好,同时也支持以 CGI 的方式加载其他语言。 #### 安装 - IIS 通常只能运行在 Windows 系统上,以 Windows 10 为例,打开控制面板,依次选择程序-启用或关闭 Windows 功能,勾选打开 Internet Information Services 服务。 启动成功后,在 “此电脑” 选项上点击右键,打开 “管理” 选项,选择 “服务和应用程序” 即可看到 IIS 的相关配置。 - #### IIS 解析特性 - - IIS 短文件名 为了兼容 16 位 MS-DOS 程序, Windows 会为文件名较长的文件生成对应的短文件名,如下所示: @@ -126,27 +110,18 @@ IIS 通常只能运行在 Windows 系统上,以 Windows 10 为例,打开控 利用这种文件机制,我们可以在 IIS 和 .net 环境下进行短文件名爆破。 - - - IIS 6.0 解析特性 -IIS 6.0 解析文件时会忽略分号后的字符串,因此 - -``` -1.asp;2.jpg 将会被解析为 -1.asp -``` +IIS 6.0 解析文件时会忽略分号后的字符串,因此 `1.asp;2.jpg` 将会被解析为 `1.asp`。 - IIS 也存在类似于 Nginx 的 CGI 解析特性 -## 如何获取 Web 服务指纹 +## 如何获取 Web 服务指纹 比赛中的信息获取往往十分重要,确定 Web 服务器指纹对于下一步的对策很重要。 #### HTTP 头识别 - -许多 Web 服务器都会在返回给用户的 HTTP 头中告知自己的服务器名称和版本。举例列出一些真实存在的包含服务器信息的 HTTP 头 - +许多 Web 服务器都会在返回给用户的 HTTP 头中告知自己的服务器名称和版本。举例列出一些真实存在的包含服务器信息的 HTTP 头: ``` Server: nginx Server: Tengine @@ -158,27 +133,18 @@ X-Powered-By: ASP.NET ``` #### 文件扩展名 - -URL 中使用的文件扩展名也能够揭示相关的服务平台和编程语言 - -``` -asp, Microsoft Active Server Pages -aspx, Microsoft ASP.NET -jsp, Java Server Pages -php, PHP -``` +URL 中使用的文件扩展名也能够揭示相关的服务平台和编程语言,如: +- `asp`:Microsoft Active Server Pages +- `aspx`:Microsoft ASP.NET +- `jsp`:Java Server Pages +- `php`:PHP #### 目录名称 - -一些子目录名称也常常表示应用程序所使用的相关技术 +一些子目录名称也常常表示应用程序所使用的相关技术。 #### 会话令牌 - -许多服务会默认生成会话令牌,通过读取 cookie 中的会话令牌可以判断所使用的技术 - -``` -JSESSIONID, JAVA -ASPSESSIONID, IIS -ASP.NET_SessionId, ASP.NET -PHPSESSID, PHP -``` \ No newline at end of file +许多服务会默认生成会话令牌,通过读取 cookie 中的会话令牌可以判断所使用的技术。如: +- `JSESSIONID`:JAVA +- `ASPSESSIONID`:IIS +- `ASP.NET_SessionId`:ASP.NET +- `PHPSESSID`:PHP diff --git a/doc/1.4.5_owasp_basic.md b/doc/1.4.5_owasp_basic.md index deb73c2..f88cf88 100644 --- a/doc/1.4.5_owasp_basic.md +++ b/doc/1.4.5_owasp_basic.md @@ -12,42 +12,37 @@ - [使用含有已知漏洞的组件](#使用含有已知漏洞的组件) - [不足的日志记录和监控](#不足的日志记录和监控) -## OWASP Project +## OWASP Project OWASP 是一个开放的 Web 安全社区,影响着 Web 安全的方方面面,OWASP 每隔一段时间就会整理更新一次 “Top 10” 的 Web 漏洞排名,对当前实际环境常见的漏洞进行罗列,虽然漏洞排名经常引起业界的争议,但是在开源环境下,该计划公布的漏洞也能够客观反映实际场景中的某些问题,因此,我们选择 OWASP Top Ten 来作为 Web 方向的漏洞入门介绍材料。 ## 注入 - 用一个不严谨的说法来形容注入攻击,就是,本应该处理用户输入字符的代码,将用户输入当作了代码来执行,常见于解释型语言。主要有以下几种形式: -|类别|说明| -|----|----| -|SQL 注入|最常见的注入形式,通过恶意拼接数据库语句,来实现非预期的功能| -|系统命令注入|通过拼接来执行非预期的操作系统指令| -|表达式语言注入|Java 中常见的命令注入执行方式| -|服务端模板注入|使用模板引擎的语言常见的注入形式| +| 类别 | 说明 | +| --- | --- | +| SQL 注入 | 最常见的注入形式,通过恶意拼接数据库语句,来实现非预期的功能 | +| 系统命令注入 | 通过拼接来执行非预期的操作系统指令 | +| 表达式语言注入 | Java 中常见的命令注入执行方式 | +| 服务端模板注入 | 使用模板引擎的语言常见的注入形式 | -一个简单的例子如下所示,这是一段身份认证常见的代码 - -``` +一个简单的例子如下所示,这是一段身份认证常见的代码: +```sql SELECT * FROM users WHERE username = 'admin' and password = '123456' ``` 这个查询接收用户输入的账号和密码,放入数据库中进行查询,如果查询有结果则允许用户登录。在这种情况下,攻击者可以注入用户名或密码字段,来修改整个 SQL 语句的逻辑,用户可以提交这样的用户名: - -``` +```sql admin' -- - ``` 这时,应用程序将执行以下查询: - -``` +```sql SELECT * FROM users WHERE username = 'admin' -- -' and password = '123456' ``` 这里使用了 SQL 语句中的注释符(--),将密码部分查询注释掉,因此上面语句等同于: - -``` +```sql SELECT * FROM users WHERE username = 'admin' ``` @@ -55,24 +50,22 @@ SELECT * FROM users WHERE username = 'admin' ## 失效的身份认证 - 身份认证对于 Web 应用程序尤为重要,它是鉴别用户权限并授权的重要依据。但是,由于设计缺陷,许多登陆窗口缺乏验证码机制,导致攻击者可以低成本的对用户口令进行爆破攻击。另一方面,大量存在的弱口令或默认口令使得攻击者可以轻易的猜测出用户的常用口令,窃取用户权限。 当用户身份得到确定后,通常会使用会话来保持一定时间的权限,避免用户短时间内需要多次重复认证。但是,如果会话 ID 处理不当,有可能导致攻击者获取会话 ID 进行登录。 -## 敏感数据泄露 +## 敏感数据泄露 一种场景是由于没有进行科学的加密方法,导致敏感数据以明文形式泄露。另一种场景是由于人为的管理不当,导致个人信息、登录凭证泄漏到公网中,常见的敏感数据泄露包括网站备份文件泄露、代码仓库泄露、硬编码凭证于代码中导致的泄露。 比如,在 Github 中搜索口令或者 API 关键字,可以发现大量私人的凭证直接写在代码中被上传到 Github 仓库中。 -## XML 外部实体 +## XML 外部实体 从某种意义上说,XXE 也是一种注入攻击。通过利用 XML 处理器对外部实体的处理机制,将用户的外部实体输入代替已定义的实体引用,执行恶意代码。 一个典型的 XXE 攻击如下所示: - -``` +```http POST /AjaxSearch.ashx HTTP/1.1 Host: test.com Content-Type: text/xml; @@ -83,16 +76,16 @@ Content-Type: text/xml; 我们创建了一个外部引用文档类型定义去访问一个敏感的系统文件,而这个外部引用会在应用程序中替代已经命名的实体去执行,最终获取到敏感文件,如果这个时候的执行结果会返回给用户,那么用户就可以看到敏感文件中的内容。 -## 失效的访问控制 +## 失效的访问控制 如果采用安全的代码框架编写模式,很有可能会造成访问控制失效问题,比如某一个需要用户登录才能访问的主页面,其中的某些功能实现的页面并没有添加权限认证过程,导致虽然攻击者无法访问主页面,但却能够访问到功能页面执行功能函数。 另一种常见的漏洞就是用户权限跨越,典型的方式是通过明文的 ID 数字来赋予用户权限,攻击者可以修改 ID 号来获取任意用户权限。 -![](../pic/1.4.5_access_control) +![](../pic/1.4.5_access_control.png) + ## 安全配置错误 - 由于配置疏忽,导致一些额外的信息、账户、文件可以被攻击者获取所导致的漏洞。常见的就是由于配置不当导致的目录遍历。 使用如下语句在 Google 中可以搜索到可目录遍历的网站,当然,许多网站也使用这种目录遍历的方式提供用户下载服务。 @@ -101,51 +94,42 @@ Content-Type: text/xml; intitle:index of ``` -## 跨站脚本 +## 跨站脚本 跨站脚本攻击(XSS)通过插入恶意脚本代码来窃取用户信息,获取用户权限以及配合其他漏洞发动更加复杂的攻击,一个最基本的 XSS 攻击如下所示,恶意脚本在 script 标签内,这一段脚本将会弹出你在当前页面上的 cookie 信息。 -``` +```html ``` XSS 漏洞根据表现形式的不同,主要有以下三种类型。 -### 反射型 XSS - +#### 反射型 XSS 有时,开发者会将一些用户可控的输入返回到网页中,如果返回的位置能够插入脚本语言或者触发事件,就存在反射型 XSS,通常攻击者发动这类攻击时需要受害者进行交互,因此这种攻击存在一定的局限性。 -### 存储型 XSS - +#### 存储型 XSS 存储型 XSS 是指当页面从持久化存储中读取内容并显示时,如果攻击者能够将 XSS 攻击代码写入持久化存储中,那么当任意用户访问漏洞页面时,都将触发恶意代码,因此,这种攻击具有更加严重的风险。 -### DOM 型 XSS - +#### DOM 型 XSS DOM 型 XSS 是由于攻击者可控的内容被加入到了正常的 JS 的框架或者 API 中导致的漏洞。 -## 不安全的反序列化 +## 不安全的反序列化 序列化是一种数据对象传递手段,在传递数据值的同时保留了数据的结构属性。但是,如果在数据传递过程中处理不当,导致用户可控序列数据,在数据反序列化过程中就有可能造成命令执行或者越权行为。由于包括 Java、Python、PHP 等在内的语言都包含序列化和反序列化功能,根据不同的语言特性,利用方法有细微差距。 -## 使用含有已知漏洞的组件 +## 使用含有已知漏洞的组件 供应链安全是比较热门的话题,由于许多开源库被广泛用于各大社区、商业软件中,同时有部分的开源库并未得到有效维护,由此带来的供应链安全导致许多用户范围很广的软件存在着隐患。 当 0 day 漏洞公布后,一些场景无法及时的打补丁,也会使自身容易被攻击者利用。 -## 不足的日志记录和监控 +## 不足的日志记录和监控 对系统、服务日志的有效监控会增加攻击者的入侵成本,因此,及时有效的日志记录、日志审计也应该是安全建设的重要环节。 需要强调的是,有时不足的日志记录方式还会产生严重的漏洞利用点,有可能被攻击者用来传递 Webshell。 + ## 参考资料 - -[2017-owasp-top-10](http://www.owasp.org.cn/owasp-project/2017-owasp-top-10) - -《黑客攻防技术宝典 - Web 实战篇》 - - - - - +- [2017-owasp-top-10](http://www.owasp.org.cn/owasp-project/2017-owasp-top-10) +- 《黑客攻防技术宝典 - Web 实战篇》 diff --git a/doc/1.4_web_basic.md b/doc/1.4_web_basic.md index 7611eda..5b63d98 100644 --- a/doc/1.4_web_basic.md +++ b/doc/1.4_web_basic.md @@ -1,8 +1,8 @@ # 1.4 Web 安全基础 -- [1.4.1 HTML基础] -- [1.4.2 HTTP协议基础] -- [1.4.3 JavaScript基础] -- [1.4.4 常见Web服务器基础] -- [1.4.5 OWASP Top Ten Project漏洞基础] -- [1.4.6 PHP源码审计基础] +- [1.4.1 HTML 基础](1.4.1_html_basic.md) +- [1.4.2 HTTP 协议基础](1.4.2_http_basic.md) +- [1.4.3 JavaScript 基础](1.4.3_javascript_basic.md) +- [1.4.4 常见 Web 服务器基础](1.4.4_webserver_basic.md) +- [1.4.5 OWASP Top Ten Project 漏洞基础](1.4.5_owasp_basic.md) +- [1.4.6 PHP 源码审计基础](1.4.6_php_basic.md) diff --git a/doc/1_basic.md b/doc/1_basic.md index 1d9518a..f7344b8 100644 --- a/doc/1_basic.md +++ b/doc/1_basic.md @@ -4,6 +4,12 @@ - [1.2 学习方法](1.2_how_to_learn.md) - [1.3 Linux 基础](1.3_linux_basic.md) - [1.4 Web 安全基础](1.4_web_basic.md) + - [1.4.1 HTML 基础](1.4.1_html_basic.md) + - [1.4.2 HTTP 协议基础](1.4.2_http_basic.md) + - [1.4.3 JavaScript 基础](1.4.3_javascript_basic.md) + - [1.4.4 常见 Web 服务器基础](1.4.4_webserver_basic.md) + - [1.4.5 OWASP Top Ten Project 漏洞基础](1.4.5_owasp_basic.md) + - [1.4.6 PHP 源码审计基础](1.4.6_php_basic.md) - [1.5 逆向工程基础](1.5_reverse_basic.md) - [1.5.1 C 语言基础](1.5.1_c_basic.md) - [1.5.2 x86/x86-64 汇编基础](1.5.2_x86&x64.md) diff --git a/doc/3.3.10_windows_kernel_exploit.md b/doc/3.3.10_windows_kernel_exploit.md new file mode 100644 index 0000000..0009790 --- /dev/null +++ b/doc/3.3.10_windows_kernel_exploit.md @@ -0,0 +1,8 @@ +# 3.3.10 Windows 内核漏洞利用 + +- [参考资料](#参考资料) + + +## 参考资料 +- [HackSys Extreme Vulnerable Driver](https://github.com/hacksysteam/HackSysExtremeVulnerableDriver) +- [windows-kernel-exploits](https://github.com/SecWiki/windows-kernel-exploits) diff --git a/doc/3.3.8_kernel_rop.md b/doc/3.3.8_kernel_rop.md new file mode 100644 index 0000000..a316516 --- /dev/null +++ b/doc/3.3.8_kernel_rop.md @@ -0,0 +1,8 @@ +# 3.3.8 内核 ROP + +- [参考资料](#参考资料) + + +## 参考资料 +- [Linux Kernel ROP - Ropping your way to # (Part 1)](https://www.trustwave.com/Resources/SpiderLabs-Blog/Linux-Kernel-ROP---Ropping-your-way-to---(Part-1)/) +- [Linux Kernel ROP - Ropping your way to # (Part 2)](https://www.trustwave.com/Resources/SpiderLabs-Blog/Linux-Kernel-ROP---Ropping-your-way-to---(Part-2)/) diff --git a/doc/3.3.8_windows_kernel_exploit.md b/doc/3.3.8_windows_kernel_exploit.md deleted file mode 100644 index 543a124..0000000 --- a/doc/3.3.8_windows_kernel_exploit.md +++ /dev/null @@ -1,5 +0,0 @@ -# 3.3.8 Windows 内核漏洞利用 - - -## 参考资料 -- [HackSys Extreme Vulnerable Driver](https://github.com/hacksysteam/HackSysExtremeVulnerableDriver) diff --git a/doc/3.3.9_linux_kernel_exploit.md b/doc/3.3.9_linux_kernel_exploit.md new file mode 100644 index 0000000..fc97e46 --- /dev/null +++ b/doc/3.3.9_linux_kernel_exploit.md @@ -0,0 +1,7 @@ +# 3.3.9 Linux 内核漏洞利用 + +- [参考资料](#参考资料) + + +## 参考资料 +- [linux-kernel-exploits](https://github.com/SecWiki/linux-kernel-exploits) diff --git a/doc/3.3_pwn.md b/doc/3.3_pwn.md index cbfe413..dcc28bd 100644 --- a/doc/3.3_pwn.md +++ b/doc/3.3_pwn.md @@ -7,3 +7,6 @@ - [3.3.5 Linux 堆利用(上)](3.3.5_heap_exploit_1.md) - [3.3.6 Linux 堆利用(中)](3.3.6_heap_exploit_2.md) - [3.3.7 Linux 堆利用(下)](3.3.7_heap_exploit_3.md) +- [3.3.8 内核 ROP](3.3.8_kernel_rop.md) +- [3.3.9 Linux 内核漏洞利用](3.3.9_linux_kernel_exploit.md) +- [3.3.10 Windows 内核漏洞利用](3.3.10_windows_kernel_exploit.md) diff --git a/doc/6.3.1_web_hctf2017_babycrack.md b/doc/6.3.1_web_hctf2017_babycrack.md index 1152418..42fe31b 100644 --- a/doc/6.3.1_web_hctf2017_babycrack.md +++ b/doc/6.3.1_web_hctf2017_babycrack.md @@ -5,11 +5,9 @@ ## 题目解析 - 题目就不用多说了,很容易发现是 JavaScript 代码审计。 -整个文件的变量名/函数名可以看作是混淆了的,分析一下整个文件的结构 - +整个文件的变量名/函数名可以看作是混淆了的,分析一下整个文件的结构: ``` —— |- _0x180a,关键字的替换数组 @@ -23,12 +21,9 @@ 首先是重排,这里不需要去深究到底逻辑原理,让引擎代替你去把数组重排好即可。结合程序员计算器和 CyberChef 分析更加方便。 + ## 解题流程 - 这样我们可以直接进入 check 函数进行分析。 - - - ``` —— |- _0x2e2f8d,又一次进行数组混淆,得到一个新数组 @@ -41,13 +36,11 @@ ``` 以上部分可以看成是准备部分,这一部分的难点在于多次处理了数组,在动态调试时,很多函数如果多次执行就会产生与原答案不同的数组结构,因此,每次执行都需要重新初始化。 - ``` —— |- _0x76e1e8,以下划线分割输入,从后面分析可以得知 flag 一共有 5 段 |- _0x34f55b,这一段给出了第一个逆向的条件,结合下一句 if 条件。 ``` - 单独来分析,其实最初我看掉了一个括号,结果弄混了符号优先级,导致觉得这个条件没有意义。 这个条件是说,**第一段的最后两个字符的 16 进制和 ‘{’ 的 16 进制异或后,对第一段的长度求余应该等于 5 **。 @@ -55,22 +48,18 @@ 这里可以先进行如下猜测 第一段,已经有 ‘hctf{’ 了,这里正好去最后两位,先猜测第一段一共只有 7 位,这个猜测是后验的,先不细说。 - ``` —— |- b2c ``` 理解这个函数极为重要,通过随机输入进行测试,输出结果有些眼熟,像是 base64 但不对,比对后确定是 base32 编码,知道这个就不用再去多解读它了。同时,这里也有一个 debug 需要删除 - ``` —— |- e,第二个逆向条件 ``` - 这一句是说,**第三段做 base32 编码,取等号前的部分,再进行 16 进制和 0x53a3f32 异或等于 0x4b7c0a73 ** - ``` 计算 0x4b7c0a73^0x53a3f32=0x‭4E463541‬ ‭4E463541 => NF5A 16 进制转字符 @@ -78,14 +67,12 @@ ``` 因此,flag 暂时如下 hctf{x\_x\_iz\_x\_x} - ``` —— |- f,第三个逆向条件 ``` 这一句是说,第四段和第三段一样编码后,和 0x4b7c0a73 异或等于 0x4315332 - ``` 计算 0x4315332^0x4b7c0a73=0x‭4F4D5941 4F4D5941 => OMYA @@ -93,7 +80,6 @@ ``` flag hctf{x\_x\_iz\_s0\_x} - ``` —— |- n,f*e*第一段的长度(先不管) @@ -104,7 +90,6 @@ flag hctf{x\_x\_iz\_s0\_x} ``` 首先是,**分割的两部份长度相等,第一部分和第二部分 16 进制异或等于 0x1613 **,这个条件只能后验,也先不管。 - ``` —— |- k,输入的 ascii 码*第二段的长度 @@ -112,7 +97,6 @@ flag hctf{x\_x\_iz\_s0\_x} ``` 首先,0x2f9b5072 == 798707826‬ - ``` 798 707 826 正好分成三个,已知h是对应 ascii 码*常数, @@ -123,28 +107,24 @@ flag hctf{x\_x\_iz\_s0\_x} ``` 所以,第二段一共有 7 个字符,前四个字符为 rev3,带入上面的后验条件 0x1613 - ``` 0x726576^0x1613=0x‭727365 727365 => rse ``` flag hctf{?\_rev3rse\_iz\_s0\_?} - ``` —— |- m,第五个逆向条件,第五段的前四位和第一段的长度有关 ``` 题目的 hint 提示,每一段都有意义,因此我们这里做个爆破,假设第一段的长度在 6-30 之间,我们可以算出 n,在用 n 去算第五段前四位。 - ``` n = f*e*(6-30) 第五段前四位 = n % 0x2f9b5072 + 0x48a05362 ``` 代码: - ``` import binascii for i in range(6,31): @@ -154,7 +134,6 @@ flag hctf{?\_rev3rse\_iz\_s0\_?} ``` 结果: - ``` qK┒ h4r @@ -186,9 +165,7 @@ flag hctf{?\_rev3rse\_iz\_s0\_?} 可以看到大多数字符都没有意义,除了 h4r 让人遐想联翩,可惜还是不全,但是结合已经分析出的 flag,猜测应该是 h4rd。 - flag hctf{??\_rev3rse\_iz\_s0\_h4rd?} - ``` —— |- _0x5a6d56,将输入重复指定次数组合 @@ -202,21 +179,18 @@ flag hctf{??\_rev3rse\_iz\_s0\_h4rd?} 5. 结合 hint 由以上条件可以推出以下 flag - ``` hctf{??_rev3ser_iz_s0_h4rd2?3??3333} ``` 先假设 2 和 3 之间没有数字了,这时 7-8 位还未知但是 7-8 位相同,这时的方程 - ``` 而且在这里,由于直接把 0x 去掉,所以 x 的 16 进制一定全为数字 字符拼接 {hex(x)hex(x)} = ascii(x)*13*5 ``` 爆破代码: - -``` +```python import binascii for i in range(1,128): @@ -226,19 +200,19 @@ flag hctf{??\_rev3rse\_iz\_s0\_h4rd?} print chr(i) except: continue - - output: e +``` +output: +``` + e ``` 验证前面的后验条件可以确定如下 flag - ``` hctf{??_rev3ser_iz_s0_h4rd23ee3333} ``` 只剩下最前面的两位,为了方便,利用题目提供的 sha256 结果,我就不回溯条件在判断,直接进行碰撞。 - -``` +```python import hashlib a = 'hctf{' @@ -258,7 +232,6 @@ flag hctf{??\_rev3rse\_iz\_s0\_h4rd?} ``` output: - ``` hctf{j5_rev3rse_iz_s0_h4rd23ee3333} -``` \ No newline at end of file +``` diff --git a/doc/7.1.7_binutils_2018-6323.md b/doc/7.1.7_binutils_2018-6323.md index eacb681..96f23cd 100644 --- a/doc/7.1.7_binutils_2018-6323.md +++ b/doc/7.1.7_binutils_2018-6323.md @@ -1,4 +1,4 @@ -# 7.1.7 [CVE-2018-6323] GNU binutils 2.26.1 Integer Overflow +# 7.1.7 [CVE-2018-6323] GNU binutils 2.29.1 Integer Overflow - [漏洞描述](#漏洞描述) - [漏洞复现](#漏洞复现) @@ -6,26 +6,28 @@ - [参考资料](#参考资料) -[下载文件](../src/exploit/7.1.6_dnstracer_2017-9430) +[下载文件](../src/exploit/7.1.7_binutils_2018-6323) ## 漏洞描述 +二进制文件描述符(BFD)库(也称为libbfd)中头文件 `elfcode.h` 中的 `elf_object_p()` 函数(binutils-2.29.1 之前)具有无符号整数溢出,溢出的原因是没有使用 `bfd_size_type` 乘法。精心制作的 ELF 文件可能导致拒绝服务攻击。 + ## 漏洞复现 | |推荐使用的环境 | 备注 | | --- | --- | --- | -| 操作系统 | Ubuntu 16.04 | 体系结构:64 位 | +| 操作系统 | Ubuntu 16.04 | 体系结构:32 位 | | 调试器 | gdb-peda| 版本号:7.11.1 | -| 漏洞软件 | binutils | 版本号:2.26.1 | +| 漏洞软件 | binutils | 版本号:2.29.1 | -编译安装 binutils: +系统自带的版本是 2.26.1,我们这里编译安装有漏洞的最后一个版本 2.29.1: ``` -$ wget https://ftp.gnu.org/gnu/binutils/binutils-2.26.1.tar.gz -$ tar zxvf binutils-2.26.1.tar.gz -$ cd binutils-2.26.1/ +$ wget https://ftp.gnu.org/gnu/binutils/binutils-2.29.1.tar.gz +$ tar zxvf binutils-2.29.1.tar.gz +$ cd binutils-2.29.1/ $ ./configure --enable-64-bit-bfd $ make && sudo make install -$ file /usr/local/bin/objdump -/usr/local/bin/objdump: 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]=72a5ff5705687fd1aa6ee58aff08d57b87694cb4, not stripped +$ file /usr/local/bin/objdump +/usr/local/bin/objdump: 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]=c2e0c7f5040cd6798b708cb29cfaeb8c28d8262b, not stripped ``` 使用 PoC 如下: @@ -51,49 +53,48 @@ os.system("objdump -x test") ``` $ python poc.py objdump: test: File truncated -*** Error in `objdump': free(): invalid pointer: 0x09803aa8 *** +*** Error in `objdump': free(): invalid pointer: 0x09b99aa8 *** ======= Backtrace: ========= -/lib/i386-linux-gnu/libc.so.6(+0x67377)[0xb75ef377] -/lib/i386-linux-gnu/libc.so.6(+0x6d2f7)[0xb75f52f7] -/lib/i386-linux-gnu/libc.so.6(+0x6dc31)[0xb75f5c31] -objdump[0x81421cb] -objdump[0x8091ab0] -objdump[0x809349c] -objdump[0x809400a] -objdump[0x80522aa] -objdump[0x804c17e] -/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf7)[0xb75a0637] -objdump[0x804c38a] +/lib/i386-linux-gnu/libc.so.6(+0x67377)[0xb7e35377] +/lib/i386-linux-gnu/libc.so.6(+0x6d2f7)[0xb7e3b2f7] +/lib/i386-linux-gnu/libc.so.6(+0x6dc31)[0xb7e3bc31] +objdump[0x814feab] +objdump[0x8096c10] +objdump[0x80985fc] +objdump[0x8099257] +objdump[0x8052791] +objdump[0x804c1af] +/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf7)[0xb7de6637] +objdump[0x804c3ca] ======= Memory map: ======== -08048000-0822c000 r-xp 00000000 08:01 270806 /usr/local/bin/objdump -0822c000-0822d000 r--p 001e3000 08:01 270806 /usr/local/bin/objdump -0822d000-08231000 rw-p 001e4000 08:01 270806 /usr/local/bin/objdump -08231000-08237000 rw-p 00000000 00:00 0 -09802000-09823000 rw-p 00000000 00:00 0 [heap] -b7200000-b7221000 rw-p 00000000 00:00 0 -b7221000-b7300000 ---p 00000000 00:00 0 -b7353000-b736f000 r-xp 00000000 08:01 394789 /lib/i386-linux-gnu/libgcc_s.so.1 -b736f000-b7370000 rw-p 0001b000 08:01 394789 /lib/i386-linux-gnu/libgcc_s.so.1 -b7387000-b7587000 r--p 00000000 08:01 141924 /usr/lib/locale/locale-archive -b7587000-b7588000 rw-p 00000000 00:00 0 -b7588000-b7738000 r-xp 00000000 08:01 394751 /lib/i386-linux-gnu/libc-2.23.so -b7738000-b773a000 r--p 001af000 08:01 394751 /lib/i386-linux-gnu/libc-2.23.so -b773a000-b773b000 rw-p 001b1000 08:01 394751 /lib/i386-linux-gnu/libc-2.23.so -b773b000-b773e000 rw-p 00000000 00:00 0 -b773e000-b7741000 r-xp 00000000 08:01 394775 /lib/i386-linux-gnu/libdl-2.23.so -b7741000-b7742000 r--p 00002000 08:01 394775 /lib/i386-linux-gnu/libdl-2.23.so -b7742000-b7743000 rw-p 00003000 08:01 394775 /lib/i386-linux-gnu/libdl-2.23.so -b7751000-b7752000 rw-p 00000000 00:00 0 -b7752000-b7759000 r--s 00000000 08:01 139343 /usr/lib/i386-linux-gnu/gconv/gconv-modules.cache -b7759000-b775a000 r--p 00741000 08:01 141924 /usr/lib/locale/locale-archive -b775a000-b775c000 rw-p 00000000 00:00 0 -b775c000-b775e000 r--p 00000000 00:00 0 [vvar] -b775e000-b7760000 r-xp 00000000 00:00 0 [vdso] -b7760000-b7782000 r-xp 00000000 08:01 394723 /lib/i386-linux-gnu/ld-2.23.so -b7782000-b7783000 rw-p 00000000 00:00 0 -b7783000-b7784000 r--p 00022000 08:01 394723 /lib/i386-linux-gnu/ld-2.23.so -b7784000-b7785000 rw-p 00023000 08:01 394723 /lib/i386-linux-gnu/ld-2.23.so -bf85b000-bf87c000 rw-p 00000000 00:00 0 [stack] +08048000-08245000 r-xp 00000000 08:01 265097 /usr/local/bin/objdump +08245000-08246000 r--p 001fc000 08:01 265097 /usr/local/bin/objdump +08246000-0824b000 rw-p 001fd000 08:01 265097 /usr/local/bin/objdump +0824b000-08250000 rw-p 00000000 00:00 0 +09b98000-09bb9000 rw-p 00000000 00:00 0 [heap] +b7a00000-b7a21000 rw-p 00000000 00:00 0 +b7a21000-b7b00000 ---p 00000000 00:00 0 +b7b99000-b7bb5000 r-xp 00000000 08:01 394789 /lib/i386-linux-gnu/libgcc_s.so.1 +b7bb5000-b7bb6000 rw-p 0001b000 08:01 394789 /lib/i386-linux-gnu/libgcc_s.so.1 +b7bcd000-b7dcd000 r--p 00000000 08:01 133406 /usr/lib/locale/locale-archive +b7dcd000-b7dce000 rw-p 00000000 00:00 0 +b7dce000-b7f7e000 r-xp 00000000 08:01 395148 /lib/i386-linux-gnu/libc-2.23.so +b7f7e000-b7f80000 r--p 001af000 08:01 395148 /lib/i386-linux-gnu/libc-2.23.so +b7f80000-b7f81000 rw-p 001b1000 08:01 395148 /lib/i386-linux-gnu/libc-2.23.so +b7f81000-b7f84000 rw-p 00000000 00:00 0 +b7f84000-b7f87000 r-xp 00000000 08:01 395150 /lib/i386-linux-gnu/libdl-2.23.so +b7f87000-b7f88000 r--p 00002000 08:01 395150 /lib/i386-linux-gnu/libdl-2.23.so +b7f88000-b7f89000 rw-p 00003000 08:01 395150 /lib/i386-linux-gnu/libdl-2.23.so +b7f97000-b7f98000 rw-p 00000000 00:00 0 +b7f98000-b7f9f000 r--s 00000000 08:01 149142 /usr/lib/i386-linux-gnu/gconv/gconv-modules.cache +b7f9f000-b7fa0000 r--p 002d4000 08:01 133406 /usr/lib/locale/locale-archive +b7fa0000-b7fa1000 rw-p 00000000 00:00 0 +b7fa1000-b7fa4000 r--p 00000000 00:00 0 [vvar] +b7fa4000-b7fa6000 r-xp 00000000 00:00 0 [vdso] +b7fa6000-b7fc9000 r-xp 00000000 08:01 395146 /lib/i386-linux-gnu/ld-2.23.so +b7fc9000-b7fca000 r--p 00022000 08:01 395146 /lib/i386-linux-gnu/ld-2.23.so +b7fca000-b7fcb000 rw-p 00023000 08:01 395146 /lib/i386-linux-gnu/ld-2.23.so +bff3a000-bff5b000 rw-p 00000000 00:00 0 [stack] Aborted (core dumped) ``` @@ -105,6 +106,353 @@ objdump: test: File format not recognized ## 漏洞分析 +首先看一下这个引起崩溃的二进制文件,它作为一个可重定位文件,本来不应该有 program headers,但这里的 Number of program headers 这一项被修改为一个很大的值,已经超过了程序在内存中的范围: +``` +$ file test +test: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped +$ readelf -h test | grep program +readelf: Error: Out of memory reading 536870912 program headers + Start of program headers: 0 (bytes into file) + Size of program headers: 0 (bytes) + Number of program headers: 65535 (536870912) +``` + +objdump 用于显示一个或多个目标文件的各种信息,通常用作反汇编器,但也能显示文件头,符号表,重定向等信息。objdump 的执行流程是这样的: +1. 首先检查命令行参数,通过 switch 语句选择要被显示的信息。 +2. 剩下的参数被默认为目标文件,它们通过 `display_bfd()` 函数进行排序。 +3. 目标文件的文件类型和体系结构通过 `bfd_check_format()` 函数来确定。如果被成功识别,则 `dump_bfd()` 函数被调用。 +4. `dump_bfd()` 依次调用单独的函数来显示相应的信息。 + +回溯栈调用情况: +``` +gdb-peda$ r -x test +gdb-peda$ bt +#0 0xb7fd9ce5 in __kernel_vsyscall () +#1 0xb7e2eea9 in __GI_raise (sig=0x6) at ../sysdeps/unix/sysv/linux/raise.c:54 +#2 0xb7e30407 in __GI_abort () at abort.c:89 +#3 0xb7e6a37c in __libc_message (do_abort=0x2, + fmt=0xb7f62e54 "*** Error in `%s': %s: 0x%s ***\n") + at ../sysdeps/posix/libc_fatal.c:175 +#4 0xb7e702f7 in malloc_printerr (action=, + str=0xb7f5f943 "free(): invalid pointer", ptr=, + ar_ptr=0xb7fb5780 ) at malloc.c:5006 +#5 0xb7e70c31 in _int_free (av=0xb7fb5780 , p=, + have_lock=0x0) at malloc.c:3867 +#6 0x0814feab in objalloc_free (o=0x8250800) at ./objalloc.c:187 +#7 0x08096c10 in bfd_hash_table_free (table=0x8250a4c) at hash.c:426 +#8 0x080985fc in _bfd_delete_bfd (abfd=abfd@entry=0x8250a08) at opncls.c:125 +#9 0x08099257 in bfd_close_all_done (abfd=0x8250a08) at opncls.c:773 +#10 0x08052791 in display_file (filename=0xbffff136 "test", target=, + last_file=0x1) at ./objdump.c:3726 +#11 0x0804c1af in main (argc=0x3, argv=0xbfffef04) at ./objdump.c:4015 +#12 0xb7e1b637 in __libc_start_main (main=0x804ba50
, argc=0x3, argv=0xbfffef04, + init=0x8150fd0 <__libc_csu_init>, fini=0x8151030 <__libc_csu_fini>, + rtld_fini=0xb7fea880 <_dl_fini>, stack_end=0xbfffeefc) at ../csu/libc-start.c:291 +#13 0x0804c3ca in _start () +``` + +一步一步追踪函数调用: +```c +// binutils/objdump.c + +int +main (int argc, char **argv) +{ + [...] + while ((c = getopt_long (argc, argv, + "pP:ib:m:M:VvCdDlfFaHhrRtTxsSI:j:wE:zgeGW::", + long_options, (int *) 0)) + != EOF) + { + switch (c) + { + [...] + case 'x': + dump_private_headers = TRUE; + dump_symtab = TRUE; + dump_reloc_info = TRUE; + dump_file_header = TRUE; + dump_ar_hdrs = TRUE; + dump_section_headers = TRUE; + seenflag = TRUE; + break; + [...] + } + } + + if (formats_info) + exit_status = display_info (); + else + { + if (optind == argc) + display_file ("a.out", target, TRUE); + else + for (; optind < argc;) + { + display_file (argv[optind], target, optind == argc - 1); + optind++; + } + } + + [...] +} +``` +```c +// binutils/objdump.c + +static void +display_file (char *filename, char *target) +{ + bfd *file; + + [...] + file = bfd_openr (filename, target); + [...] + display_any_bfd (file, 0); + + if (! last_file) + bfd_close (file); + else + bfd_close_all_done (file); +} +``` +```c +// binutils/objdump.c + +static void +display_any_bfd (bfd *file, int level) +{ + /* Decompress sections unless dumping the section contents. */ + if (!dump_section_contents) + file->flags |= BFD_DECOMPRESS; + + /* If the file is an archive, process all of its elements. */ + if (bfd_check_format (file, bfd_archive)) + { + [...] + } + else + display_object_bfd (file); +} +``` + +最关键的部分,读取 program headers 的逻辑如下: +```c +// binutils/objdump.c + + /* Read in the program headers. */ + if (i_ehdrp->e_phnum == 0) + elf_tdata (abfd)->phdr = NULL; + else + { + Elf_Internal_Phdr *i_phdr; + unsigned int i; + +#ifndef BFD64 + if (i_ehdrp->e_phnum > ((bfd_size_type) -1) / sizeof (*i_phdr)) + goto got_wrong_format_error; +#endif + amt = i_ehdrp->e_phnum * sizeof (*i_phdr); // <-- 整型溢出点 + elf_tdata (abfd)->phdr = (Elf_Internal_Phdr *) bfd_alloc (abfd, amt); + if (elf_tdata (abfd)->phdr == NULL) + goto got_no_match; + if (bfd_seek (abfd, (file_ptr) i_ehdrp->e_phoff, SEEK_SET) != 0) + goto got_no_match; + i_phdr = elf_tdata (abfd)->phdr; + for (i = 0; i < i_ehdrp->e_phnum; i++, i_phdr++) + { + Elf_External_Phdr x_phdr; + + if (bfd_bread (&x_phdr, sizeof x_phdr, abfd) != sizeof x_phdr) + goto got_no_match; + elf_swap_phdr_in (abfd, &x_phdr, i_phdr); + } + } +``` + +溢出点乘法运算前,eax 为我们在二进制文件中伪造的数值 `0x20000000`: +``` +gdb-peda$ ni +[----------------------------------registers-----------------------------------] +EAX: 0x20000000 ('') +EBX: 0x8250a08 --> 0x8250810 ("test") +ECX: 0xd ('\r') +EDX: 0x5f ('_') +ESI: 0x8250ac8 --> 0x464c457f +EDI: 0xd ('\r') +EBP: 0x81ca560 --> 0x81c9429 ("elf32-i386") +ESP: 0xbfffec20 --> 0xb7fe97eb (<_dl_fixup+11>: add esi,0x15815) +EIP: 0x80aeba0 (: imul eax,eax,0x38) +EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) +[-------------------------------------code-------------------------------------] + 0x80aeb90 : or DWORD PTR [ebx+0x28],0x800 + 0x80aeb97 : jmp 0x80ae613 + 0x80aeb9c : lea esi,[esi+eiz*1+0x0] +=> 0x80aeba0 : imul eax,eax,0x38 + 0x80aeba3 : sub esp,0x4 + 0x80aeba6 : xor edx,edx + 0x80aeba8 : push edx + 0x80aeba9 : push eax +[------------------------------------stack-------------------------------------] +0000| 0xbfffec20 --> 0xb7fe97eb (<_dl_fixup+11>: add esi,0x15815) +0004| 0xbfffec24 --> 0x8250ac8 --> 0x464c457f +0008| 0xbfffec28 --> 0xd ('\r') +0012| 0xbfffec2c --> 0x0 +0016| 0xbfffec30 --> 0x8250a0c --> 0x81ca560 --> 0x81c9429 ("elf32-i386") +0020| 0xbfffec34 --> 0x82482a0 --> 0x9 ('\t') +0024| 0xbfffec38 --> 0x8250a08 --> 0x8250810 ("test") +0028| 0xbfffec3c --> 0x81ca560 --> 0x81c9429 ("elf32-i386") +[------------------------------------------------------------------------------] +Legend: code, data, rodata, value +780 elf_tdata (abfd)->phdr = (Elf_Internal_Phdr *) bfd_alloc (abfd, amt); +``` +做乘法运算,`0x20000000 * 0x38 = 0x700000000`,产生溢出。截断后高位的 `0x7` 被丢弃, eax 为 `0x00000000`: +``` +gdb-peda$ ni +[----------------------------------registers-----------------------------------] +EAX: 0x0 +EBX: 0x8250a08 --> 0x8250810 ("test") +ECX: 0xd ('\r') +EDX: 0x5f ('_') +ESI: 0x8250ac8 --> 0x464c457f +EDI: 0xd ('\r') +EBP: 0x81ca560 --> 0x81c9429 ("elf32-i386") +ESP: 0xbfffec20 --> 0xb7fe97eb (<_dl_fixup+11>: add esi,0x15815) +EIP: 0x80aeba3 (: sub esp,0x4) +EFLAGS: 0xa07 (CARRY PARITY adjust zero sign trap INTERRUPT direction OVERFLOW) +[-------------------------------------code-------------------------------------] + 0x80aeb97 : jmp 0x80ae613 + 0x80aeb9c : lea esi,[esi+eiz*1+0x0] + 0x80aeba0 : imul eax,eax,0x38 +=> 0x80aeba3 : sub esp,0x4 + 0x80aeba6 : xor edx,edx + 0x80aeba8 : push edx + 0x80aeba9 : push eax + 0x80aebaa : push ebx +[------------------------------------stack-------------------------------------] +0000| 0xbfffec20 --> 0xb7fe97eb (<_dl_fixup+11>: add esi,0x15815) +0004| 0xbfffec24 --> 0x8250ac8 --> 0x464c457f +0008| 0xbfffec28 --> 0xd ('\r') +0012| 0xbfffec2c --> 0x0 +0016| 0xbfffec30 --> 0x8250a0c --> 0x81ca560 --> 0x81c9429 ("elf32-i386") +0020| 0xbfffec34 --> 0x82482a0 --> 0x9 ('\t') +0024| 0xbfffec38 --> 0x8250a08 --> 0x8250810 ("test") +0028| 0xbfffec3c --> 0x81ca560 --> 0x81c9429 ("elf32-i386") +[------------------------------------------------------------------------------] +Legend: code, data, rodata, value +0x080aeba3 780 elf_tdata (abfd)->phdr = (Elf_Internal_Phdr *) bfd_alloc (abfd, amt); +``` +于是,在随后的 `bfd_alloc()` 调用时,第二个参数即分配的大小为 0,导致了最后拒绝服务攻击的发生: +```c +// bfd/opncls.c + +void *bfd_alloc (bfd *abfd, bfd_size_type wanted); +``` +``` +gdb-peda$ ni +[----------------------------------registers-----------------------------------] +EAX: 0x0 +EBX: 0x8250a08 --> 0x8250810 ("test") +ECX: 0xd ('\r') +EDX: 0x0 +ESI: 0x8250ac8 --> 0x464c457f +EDI: 0xd ('\r') +EBP: 0x81ca560 --> 0x81c9429 ("elf32-i386") +ESP: 0xbfffec10 --> 0x8250a08 --> 0x8250810 ("test") +EIP: 0x80aebab (: call 0x8099540 ) +EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) +[-------------------------------------code-------------------------------------] + 0x80aeba8 : push edx + 0x80aeba9 : push eax + 0x80aebaa : push ebx +=> 0x80aebab : call 0x8099540 + 0x80aebb0 : mov DWORD PTR [esi+0x50],eax + 0x80aebb3 : mov eax,DWORD PTR [ebx+0xa0] + 0x80aebb9 : add esp,0x10 + 0x80aebbc : mov ecx,DWORD PTR [eax+0x50] +Guessed arguments: +arg[0]: 0x8250a08 --> 0x8250810 ("test") +arg[1]: 0x0 +arg[2]: 0x0 +[------------------------------------stack-------------------------------------] +0000| 0xbfffec10 --> 0x8250a08 --> 0x8250810 ("test") +0004| 0xbfffec14 --> 0x0 +0008| 0xbfffec18 --> 0x0 +0012| 0xbfffec1c --> 0x80aea71 (: mov eax,DWORD PTR [esi+0x28]) +0016| 0xbfffec20 --> 0xb7fe97eb (<_dl_fixup+11>: add esi,0x15815) +0020| 0xbfffec24 --> 0x8250ac8 --> 0x464c457f +0024| 0xbfffec28 --> 0xd ('\r') +0028| 0xbfffec2c --> 0x0 +[------------------------------------------------------------------------------] +Legend: code, data, rodata, value +0x080aebab 780 elf_tdata (abfd)->phdr = (Elf_Internal_Phdr *) bfd_alloc (abfd, amt); +``` + +#### 补丁 +该漏洞在 binutils-2.30 中被修复,补丁将 `i_ehdrp->e_shnum` 转换成 unsigned long 类型的 `bfd_size_type`,从而避免整型溢出。BFD 开发文件包含在软件包 `binutils-dev` 中: +```c +// /usr/include/bfd.h +typedef unsigned long bfd_size_type; +``` +由于存在回绕,一个无符号整数表达式永远无法求出小于零的值,因此不会产生溢出。 + +所谓回绕,可以看下面这个例子: +```c +unsigned int ui; +ui = UINT_MAX; // 在 32 位上为 4 294 967 295 +ui++; +printf("ui = %u\n", ui); // ui = 0 +ui = 0; +ui--; +printf("ui = %u\n", ui); // 在 32 位上,ui = 4 294 967 295 +``` + +```diff +$ git show 38e64b0ecc7f4ee64a02514b8d532782ac057fa2 bfd/elfcode.h +commit 38e64b0ecc7f4ee64a02514b8d532782ac057fa2 +Author: Alan Modra +Date: Thu Jan 25 21:47:41 2018 +1030 + + PR22746, crash when running 32-bit objdump on corrupted file + + Avoid unsigned int overflow by performing bfd_size_type multiplication. + + PR 22746 + * elfcode.h (elf_object_p): Avoid integer overflow. + +diff --git a/bfd/elfcode.h b/bfd/elfcode.h +index 00a9001..ea1388d 100644 +--- a/bfd/elfcode.h ++++ b/bfd/elfcode.h +@@ -680,7 +680,7 @@ elf_object_p (bfd *abfd) + if (i_ehdrp->e_shnum > ((bfd_size_type) -1) / sizeof (*i_shdrp)) + goto got_wrong_format_error; + #endif +- amt = sizeof (*i_shdrp) * i_ehdrp->e_shnum; ++ amt = sizeof (*i_shdrp) * (bfd_size_type) i_ehdrp->e_shnum; + i_shdrp = (Elf_Internal_Shdr *) bfd_alloc (abfd, amt); + if (!i_shdrp) + goto got_no_match; +@@ -776,7 +776,7 @@ elf_object_p (bfd *abfd) + if (i_ehdrp->e_phnum > ((bfd_size_type) -1) / sizeof (*i_phdr)) + goto got_wrong_format_error; + #endif +- amt = i_ehdrp->e_phnum * sizeof (*i_phdr); ++ amt = (bfd_size_type) i_ehdrp->e_phnum * sizeof (*i_phdr); + elf_tdata (abfd)->phdr = (Elf_Internal_Phdr *) bfd_alloc (abfd, amt); + if (elf_tdata (abfd)->phdr == NULL) + goto got_no_match; +``` + +打上补丁之后的 objdump 没有再崩溃: +``` +$ objdump -v | head -n 1 +GNU objdump (GNU Binutils) 2.30 +$ objdump -x test +objdump: test: Memory exhausted +``` + ## 参考资料 +- https://www.cvedetails.com/cve/CVE-2018-6323/ - [GNU binutils 2.26.1 - Integer Overflow (POC)](https://www.exploit-db.com/exploits/44035/)