chore: turn cos to cdn
This commit is contained in:
@@ -34,7 +34,7 @@ def check_pass(username, password):
|
||||
|
||||
从 `users` 表中查出 `username` 对应的 `password` 的哈希值,将其与用户传入的密码哈希值进行比对,若相等则意味着用户传入的密码与数据库中储存的密码相吻合,于是返回准许登录
|
||||
|
||||

|
||||

|
||||
|
||||
那么问题来了,在语句
|
||||
|
||||
@@ -144,7 +144,7 @@ mysql> select group_concat(id,username separator '_') from users;
|
||||
|
||||
现在我们传入 `Liki4'` 这个字符串
|
||||
|
||||

|
||||

|
||||
|
||||
很遗憾,报错了,这个查询因为 SQL 语句存在语法错误而无法完成。
|
||||
|
||||
@@ -154,7 +154,7 @@ mysql> select group_concat(id,username separator '_') from users;
|
||||
|
||||
那如果我们传入 `Liki4';#` 这个字符串,那么在拼接后的查询又是什么结果呢
|
||||
|
||||

|
||||

|
||||
|
||||
很显然,`#` 号将原本语句的 `';` 注释掉了
|
||||
|
||||
@@ -166,7 +166,7 @@ mysql> select group_concat(id,username separator '_') from users;
|
||||
|
||||
`raw_sql_danger' UNION SELECT password FROM users WHERE username = 'Liki5';#`
|
||||
|
||||

|
||||

|
||||
|
||||
<strong>真是惊人的壮举!我完全不认识这个叫 Liki5 的家伙,但我居然知道了他的密码对应的哈希值!</strong>
|
||||
|
||||
@@ -273,7 +273,7 @@ if __name__ == "__main__":
|
||||
|
||||
接下来我们进行一次常规查询
|
||||
|
||||

|
||||

|
||||
|
||||
可以看到我们成功从数据库中查出了 `username` 和 `password`,并显示在返回中
|
||||
|
||||
@@ -293,7 +293,7 @@ def query(username):
|
||||
...
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
可以看到,实际执行的语句为
|
||||
|
||||
@@ -307,7 +307,7 @@ SELECT * FROM users WHERE username = '123' UNION SELECT 1, 2;#'
|
||||
|
||||
构造语句 `123' UNION SELECT DATABASE(), @@version;#`
|
||||
|
||||

|
||||

|
||||
|
||||
我们就能看到返回中包含了当前数据库名与当前数据库版本
|
||||
|
||||
@@ -317,13 +317,13 @@ SELECT * FROM users WHERE username = '123' UNION SELECT 1, 2;#'
|
||||
|
||||
> `information_schema` 库是一个 MySQL 内置数据库,储存了数据库中的一些基本信息,比如数据库名,表名,列名等一系列关键数据,SQL 注入中可以查询该库来获取数据库中的敏感信息。
|
||||
|
||||

|
||||

|
||||
|
||||
我们可以发现,当前数据库中还存在一张叫 `secret` 的表,让我们偷看一下里面存的是什么
|
||||
|
||||
构造语句 `123' UNION SELECT 1, secret_string FROM secret;#`
|
||||
|
||||

|
||||

|
||||
|
||||
好像得到了什么不得了的秘密 :-)
|
||||
|
||||
@@ -363,7 +363,7 @@ if __name__ == "__main__":
|
||||
|
||||
这样一来我们就只能知道自己是否登录成功,并不能看到查询返回的结果了
|
||||
|
||||

|
||||

|
||||
|
||||
那也就是说,我们无法直观地查看数据库中的数据了,即便查出了不该查的也看不到了 :-(
|
||||
|
||||
@@ -396,7 +396,7 @@ else:
|
||||
|
||||
> rlike 是 MySQL 中的一个关键字,是 regex 和 like 的结合体
|
||||
|
||||

|
||||

|
||||
|
||||
这里实际执行的语句就变成了
|
||||
|
||||
@@ -404,13 +404,13 @@ else:
|
||||
SELECT password FROM users WHERE username = 'Liki4' AND if(@@version rlike '^5',1,0);
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
```sql
|
||||
SELECT password FROM users WHERE username = 'Liki4' AND if(@@version rlike '^8',1,0);
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
也就是说,当 if 语句中的条件为真时,这个查询才会将 password 查询出来
|
||||
|
||||
@@ -480,7 +480,7 @@ if __name__ == "__main__":
|
||||
exp()
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
####
|
||||
|
||||
@@ -523,7 +523,7 @@ if __name__ == "__main__":
|
||||
|
||||
如果想要让布尔盲注不可用,我们可以做一个假设,假设我们并不知道账户的密码,也就无法通过登陆验证,这个时候就失去了布尔盲注最大的依赖,也就无法得知 if 表达式的真或假了。
|
||||
|
||||

|
||||

|
||||
|
||||
但,真的没办法了吗?
|
||||
|
||||
@@ -600,7 +600,7 @@ if __name__ == "__main__":
|
||||
exp()
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
### 基于报错的 SQL 注入 (TODO)
|
||||
|
||||
@@ -640,7 +640,7 @@ if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
这样一来如果 SQL 语句执行报错的话,错误信息就会被打印出来
|
||||
|
||||
@@ -671,9 +671,9 @@ MySQL 8.0 doc: [https://dev.mysql.com/doc/refman/8.0/en/](https://dev.mysql.com/
|
||||
|
||||
`Liki4';INSERT INTO users VALUES ('Liki3','01848f8e70090495a136698a41c5b37406968c648ab12133e0f256b2364b5bb5');#`
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
INSERT 语句也被成功执行了,向数据库中插入了 Liki3 的数据
|
||||
|
||||
@@ -776,7 +776,7 @@ INSERT 语句也被成功执行了,向数据库中插入了 Liki3 的数据
|
||||
在 GB2312、GBK、GB18030、BIG5、Shift_JIS 等编码下来吃掉 ASCII 字符的方法,可以用来绕过 `addslashes()`
|
||||
`id=0%df%27%20union%20select%201,2,database();`
|
||||
|
||||

|
||||

|
||||
|
||||
### information_schema 被过滤
|
||||
|
||||
@@ -795,7 +795,7 @@ select table_name from mysql.innodb_index_stats where database_name=<em>database
|
||||
select table_name from mysql.innodb_table_stats where database_name=<em>database</em>();
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
##### MySQL 5.7 的新特性
|
||||
|
||||
@@ -812,7 +812,7 @@ select table_name from sys.schema_table_statistics_with_buffer where table_schem
|
||||
select table_name from sys.x$schema_table_statistics_with_buffer where table_schema=<em>database</em>();
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
### 无列名注入
|
||||
|
||||
@@ -820,7 +820,7 @@ select table_name from sys.x$schema_table_statistics_with_buffer where table_sch
|
||||
|
||||
`select a,b from (select 1 as a, 2 as b union select * from users)x;`
|
||||
|
||||

|
||||

|
||||
|
||||
## 超脱 MySQL 之外 (TODO)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ IDA pro 是收费软件,价格极其昂贵,一套完全版人民币 10W 左
|
||||
|
||||
## 0x00 IDA 简单介绍
|
||||
|
||||

|
||||

|
||||
|
||||
IDA是一款交互式反汇编和反编译工具,其支持文件类型和文件平台丰富。
|
||||
|
||||
@@ -20,7 +20,7 @@ IDA是一款交互式反汇编和反编译工具,其支持文件类型和文
|
||||
|
||||
## 0x01 启动界面
|
||||
|
||||

|
||||

|
||||
|
||||
```
|
||||
NEW:打开IDA同时弹出对话框选择要打开的文件
|
||||
@@ -30,15 +30,15 @@ Previous,或者下面的列表项:快速打开之前的的文件
|
||||
|
||||
这里选择Go键,打开以后,将文件拖入
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
这里按我们的默认选项点击OK即可
|
||||
|
||||
## 0x02 关闭界面
|
||||
|
||||

|
||||

|
||||
|
||||
```
|
||||
第一个选项:就是不打包数据包文件,那么这些数据库文件就会分开这放。
|
||||
@@ -53,15 +53,15 @@ Previous,或者下面的列表项:快速打开之前的的文件
|
||||
|
||||
反汇编代码的图表窗口
|
||||
|
||||

|
||||

|
||||
|
||||
按**空格键**切换成文本结构的反汇编
|
||||
|
||||

|
||||

|
||||
|
||||
按**F5**进行反编译跳转至`Pseudocode`(伪代码)界面
|
||||
|
||||

|
||||

|
||||
|
||||
然后就可以分析代码逻辑了
|
||||
|
||||
@@ -71,19 +71,19 @@ Previous,或者下面的列表项:快速打开之前的的文件
|
||||
|
||||
十六进制窗口(不太常用)
|
||||
|
||||

|
||||

|
||||
|
||||
## 0x05 主界面-Structures
|
||||
|
||||
结构体窗口
|
||||
|
||||

|
||||

|
||||
|
||||
## 0x06 主界面-Enums
|
||||
|
||||
枚举类型界面
|
||||
|
||||

|
||||

|
||||
|
||||
## 0x07 主界面-Imports
|
||||
|
||||
@@ -91,23 +91,23 @@ Previous,或者下面的列表项:快速打开之前的的文件
|
||||
|
||||
可以查看当前模块用了哪些模块的哪些函数
|
||||
|
||||

|
||||

|
||||
|
||||
## 0x08 主界面-Exports
|
||||
|
||||
导出表
|
||||
|
||||

|
||||

|
||||
|
||||
## 0x09 主界面-Strings
|
||||
|
||||
按`Shift+F12`转到`String`界面,该操作会搜索程序中的字符串数据并展示
|
||||
|
||||

|
||||

|
||||
|
||||
按`Ctrl+F`后输入想要检索的字符可以快速搜索字符串
|
||||
|
||||

|
||||

|
||||
|
||||
## 0x0a 其他界面-Functions
|
||||
|
||||
@@ -115,7 +115,7 @@ Previous,或者下面的列表项:快速打开之前的的文件
|
||||
|
||||
其中一般来说`main`是程序的主要函数
|
||||
|
||||

|
||||

|
||||
|
||||
## 0x0b 其他界面-Output
|
||||
|
||||
@@ -125,13 +125,13 @@ Previous,或者下面的列表项:快速打开之前的的文件
|
||||
|
||||
另外还可以直接在下面输入python语句,方便在ida使用过程中简单的数据处理
|
||||
|
||||

|
||||

|
||||
|
||||
## 0x0c 其他界面-导航栏
|
||||
|
||||
一个二进制文件包括不同的区块,这里显示程序的不同类型数据,不同的颜色代表二进制文件中不同的块
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ Previous,或者下面的列表项:快速打开之前的的文件
|
||||
|
||||
IDA 提供可与其交互的IDA Python接口,可以使用Python做很多的辅助操作
|
||||
|
||||

|
||||

|
||||
|
||||
可以参考这篇文章了解常用的接口
|
||||
|
||||
@@ -184,17 +184,17 @@ IDA 提供可与其交互的IDA Python接口,可以使用Python做很多的辅
|
||||
|
||||
可以先在汇编代码或伪代码界面下断点,然后`F9`选择调试器,这里直接选`Local Windows Debugger`
|
||||
|
||||

|
||||

|
||||
|
||||
之后就可以用F7(单步不跳过执行)/F8(单步跳过执行)/F9(继续执行,遇到断点停止)进行调试
|
||||
|
||||

|
||||

|
||||
|
||||
### 调试Linux下的文件
|
||||
|
||||
可以先在汇编代码或伪代码界面下断点
|
||||
|
||||

|
||||

|
||||
|
||||
由于Linux下文件调试比较特殊,需要远程起一个服务器运行服务端,这里可以使用**Vmware**或者**WSL2(Windows subsystem Linux)**进行调试
|
||||
|
||||
@@ -240,21 +240,21 @@ int main() {
|
||||
|
||||
##### 将程序拖入IDA
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
##### F5分析查看伪代码
|
||||
|
||||

|
||||

|
||||
|
||||
发现有`change`和`check`的自定义函数
|
||||
|
||||
按`n`修改一下变量名
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
分别进入里面查看函数逻辑
|
||||
|
||||
@@ -262,11 +262,11 @@ int main() {
|
||||
|
||||
change函数
|
||||
|
||||

|
||||

|
||||
|
||||
check函数
|
||||
|
||||

|
||||

|
||||
|
||||
###### 静态分析逻辑
|
||||
|
||||
@@ -280,19 +280,19 @@ change函数是对输入字符串的每一个字节进行修改
|
||||
|
||||
随意的进行一些输入
|
||||
|
||||

|
||||

|
||||
|
||||
然后断下来
|
||||
|
||||

|
||||

|
||||
|
||||
F7进入函数进行单步不跳过调试
|
||||
|
||||

|
||||

|
||||
|
||||
遇到类似`strlen`等库函数可以F8单步调试跳过
|
||||
|
||||

|
||||

|
||||
|
||||
可以发现输入字符串的每一个字节的Ascii值都减小了1
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ ESP 定律的原理在于利用程序中堆栈平衡来快速找到 OEP.
|
||||
|
||||
还是上一篇的示例, 入口一句 `pushad`, 我们按下 F8 执行 `pushad` 保存寄存器状态, 我们可以在右边的寄存器窗口里发现 `ESP` 寄存器的值变为了红色, 也即值发生了改变.
|
||||
|
||||

|
||||

|
||||
|
||||
我们鼠标右击 `ESP` 寄存器的值, 也就是图中的 `0019FF64`, 选择 `HW break[ESP]` 后, 按下 `F9` 运行程序, 程序会在触发断点时断下. 如图来到了 `0040D3B0` 的位置. 这里就是上一篇我们单步跟踪时到达的位置, 剩余的就不再赘述.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#### 栈介绍
|
||||
栈是一种典型的后进先出 (Last in First Out) 的数据结构,其操作主要有压栈 (push) 与出栈 (pop) 两种操作,如下图所示(维基百科)。两种操作都操作栈顶,当然,它也有栈底。
|
||||
|
||||

|
||||

|
||||
|
||||
高级语言在运行时都会被转换为汇编程序,在汇编程序运行过程中,充分利用了栈这一数据结构。每个程序在运行时都有虚拟地址空间,其中某一部分就是该程序对应的栈,用于保存函数调用信息和局部变量。此外,常见的操作也是压栈与出栈。需要注意的是,**程序的栈是从进程地址空间的高地址向低地址增长的**。
|
||||
#### 栈溢出基本原理
|
||||
@@ -21,7 +21,7 @@ int main()
|
||||
|
||||
对于如上程序,运行后可以发现`ch`和`a`的地址相差不大(`a`和`ch`的顺序不一定固定为`a`在前`ch`在后):
|
||||
|
||||

|
||||

|
||||
|
||||
可以发现`ch`和`ch2`刚好差`8`个字节,也就是`ch`的长度。
|
||||
`ch`只有`8`个字节,那么如果我们向`ch`中写入超过`8`个字节的数据呢?很显然,会从`ch`处发生溢出,写入到`ch2`的空间中,覆盖`ch2`的内容。
|
||||
@@ -37,7 +37,7 @@ int main()
|
||||
}
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
这就是栈溢出的基本原理。
|
||||
|
||||
@@ -131,7 +131,7 @@ retn
|
||||
|
||||
可以看到其中使用`call`指令来调用`add`函数。那么该指令是如何工作的呢?其实`call`指令相当于`push next_loc;jmp loc`,通过将`call`指令下一行汇编的地址压栈的方式,等到函数调用完再取回,从而从`call`指令的下一行继续执行。由于栈地址从高向低生长,新调用的函数的局部变量生成在返回地址的上方(低地址处),因此如果我们在新函数中使用栈溢出来修改这一返回地址,如果将返回地址修改为某个函数的地址,就可以执行任意函数:
|
||||
|
||||

|
||||

|
||||
|
||||
> 注意该图中,使用32位的寄存器(EBP、ESP、EIP),实际原理一样的,并且上方为高地址,下方为低地址
|
||||
|
||||
@@ -139,22 +139,22 @@ retn
|
||||
|
||||
32位的程序,我们使用IDA来打开该题目,查看反编译代码,可以发现有非常明显的栈溢出:
|
||||
|
||||

|
||||

|
||||
|
||||
由于第`8`行`gets`函数并没有检查输入的长度和`s`的长度,我们可以轻易地通过栈溢出来控制`main`函数的返回地址。而在程序中,存在另外一个函数`secure`,在该函数中有一个后门`system("/bin/sh")`,如果我们想办法执行该后门,就可以拿到目标机器的`shell`,从而控制目标计算机。
|
||||
|
||||
由于我们需要将返回地址在标准输入中输入待测程序,而返回地址拆分成小端序的字节后经常无法手动输入到待测程序中,所以此处我们使用`pwntools`这一`python`包来方便地进行攻击。
|
||||
首先查看后门的地址:
|
||||
|
||||

|
||||

|
||||
|
||||
接着计算溢出长度,这里我们使用gdb来调试程序,图中的gdb安装了pwndbg插件,该插件在pwn调试时比较好用:
|
||||
|
||||

|
||||

|
||||
|
||||
将断点打在`gets`函数前后,可以看到此时`esp`值为`0xffffcd80`,`ebp`值为`0xffffce08`,在 IDA 中我们又可以看到`s`相对于`esp`的偏移为`+1C`,此时我们即可计算`hex(0xffffcd80+0x1c-0xffffce08)=-0x6C`,即`s`相对于`ebp`的偏移为`0x6C`,由于在`main`函数的开头有`push ebp`的操作,所以将`0x6C`再加`4`,即可到达返回地址处:
|
||||
|
||||

|
||||

|
||||
|
||||
```python
|
||||
from pwn import *
|
||||
@@ -165,7 +165,7 @@ sh.sendline(exp)
|
||||
sh.interactive() # 切换为手动交互模式
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
##### 0x1
|
||||
通过上面的学习,我们已经可以知道执行任意函数的办法,但很多情况下,对于攻击者来说,程序中并没有可用的后门函数来达到攻击的目的,因此我们需要一种手段,来让程序执行任意代码(任意汇编代码),这样就可以最高效地进行攻击。ROP(Return Oriented Programming)面向返回编程就是这样的一种技术,在栈溢出的基础上,通过在程序中寻找以retn结尾的小片段(gadgets),来改变某些寄存器、栈变量等的值,再结合Linux下的系统调用,我们就可以执行需要的任意代码。
|
||||
|
||||
Reference in New Issue
Block a user