CTF-北理工暑期培训
1 Python 快速入门
注释:
1 |
|
或:
1 |
|
用更高精度的 float
用 decimal
模块.
python 中没有 ++
和 --
bin()
将十进制转换为二进制数:
1 |
|
输出则为:
1 |
|
hex()
将十进制转换为十六进制数:
1 |
|
输出则为:
1 |
|
可连续比较:
1 |
|
字符串连接:
1 |
|
切片:
1 |
|
format
来格式化:
1 |
|
python 中的多行写法:
1 |
|
类型注解:
1 |
|
创建列表:
1 |
|
可用 pop()
和 append()
等方法.
删除任意元素:
1 |
|
是否包含:
1 |
|
元组创建:
1 |
|
其不可变.
在函数中使用全局变量, 用 global
关键字:
1 |
|
终端获取:
1 |
|
文件读写:
1 |
|
python 中, strings, tuples, numbers 都是不可更改的对象, 而 list, dict 都是可以修改的对象
关于新的内存空间的开辟:
2 古典密码学, 编码介绍
概念:
- 明文(m, plaintext)
- 密文(c, ciphertext)
- 密钥(key)
三种加密算法:
- Gen (Generate), 用算法来生成一个密钥
- Enc (Encode), 加密算法
- Dec (Decode), 解密算法
一个式子的含义:
1 |
|
用 k
加密过的明文 m
可以通过 k
解密得到原来的明文.
四种攻击方式:
- 唯密文攻击, 被攻击方向攻击方传送密文, 攻击方找共同点
- 已知明文攻击, 被攻击方向攻击方传送明密文对, 攻击方分析
- 选择明文攻击, 攻击方可以向被攻击方发送明文并得到返回的密文进行分析
- 选择密文攻击, 攻击方可以向被攻击方发送密文并得到返回的明文进行分析
古典密码学
基于算法的安全性, 算法是未知的, 一旦被已知算法, 就容易被破解.
一般分为两种加密方式:
- 字母的代换, 一一映射
- 顺序的置换, 调换字串顺序
凯撒密码 & 移位密码
如:
A
被映射为 “移动4位” 的字符 E
, 后面的都依次类推.
用穷举法解决.
仿射加密
每个字母对应一个数字 0 ~ 25
, 得到一个 key (a,b)
:
$$
\displaylines
{
\begin{aligned}
Enc: c = a \cdot m + b\ mod\ 26 \
Dec: m = a^{-1} (c-b)\ mod 26
\end{aligned}
}
$$
单字母替换密码
用词频分析解决.
3 PWN, Linux 入门
PWN 指二进制漏洞挖掘 (Binary exploitation)
payload 就是要发送到目标服务器的数据.
4 Web 基础
url 格式:
域名结构:
也就是从后往前解析.
OSI 体系结构可能有些过时, 大部分用 TCP/IP 的体系结构.
注意 TCP 的三次握手和四次挥手, 这里的次数指的是传送的包的数量.
一些工具:
- AntSword, 中国蚁剑
- BurpSuite
- Sqlmap
- hackbar
一些套路:
F12
查看 HTML 源码- Web 源码泄漏
- vim 源码泄漏, 利用如
/.index.php.swp
文件,vim -r index.php
- vim 源码泄漏, 利用如
- 查看 http 数据包 BurpSuite 看请求/响应首部
- 修改 http 请求头
- Method
- User-Agent
- Referer
- X-Forwarded-For
- Cookie
5 Pwn 汇编入门
程序编译与链接
过程:
16 位的 CPU 只能确定数据总线是 16 位的.
BP
寄存器是用来定位栈帧的基底.
Pwn 主要要覆写的内容就是 SP
, BP
和 IP
.
6 Web 爬虫入门
抓取有规律的网页.
重复处理网络工作.
与网站自动交互.
这里用 python 编写爬虫脚本, 可查看官方文档
几个推荐的平台:
- GeakforGeak
中文网站的编码可能用 gbk
或者 gb1312
.
登录时都是通过 cookie 来判断的.
常用 Get 和 Post 两种请求方法.
网页是文档树的结构.
CTF 中常用的 Python 库:
- Web
- Requests
- Socket
- Blast
- itertools
- Multiprocess
- Crypto
- gmpy2
- PyCrypto/PyCryptodome
- Pwn
- Pwntools
关于爬虫的合法性
基本每个网站都有 robots.txt
文件, 一般位于网站的根目录下. 如 http://baidu.com/robots.txt
文件内容如:
1 |
|
做 Web 题时, 注意 .zip
, .swap
和 .bak
文件. 有时也会放在 robots.txt
里面.
7 Pwn 实战
步骤:
收集信息, 如小段 gagets 的地址, glibc
的版本.
查看保护用 checksec
, 了解程序逻辑一般用 IDA
.
一般存在栈溢出的一般是 gets
, getchar
等函数.
可能存在内存地址泄漏的一般有 printf
.
辅助信息一般有可链接的 elf 文件或版本号.
很多工具都依赖 python. 一般用 Pwntools 模块, 其包含了大量需要使用的模块, 包括 pwn, pwnlib 等.
安装 pwntools
的开发版:
1 |
|
checksec 的输出如:
其中:
Arch(Architecture, 架构):指定二进制文件编译的目标架构(Architecture),例如x86、x64、ARM等。
RELRO(RElocation Read-Only, 可重定位只读性):指定二进制文件中重定位表(Relocation Table)的只读性。RELRO分为三种级别:
- No RelRO:重定位表无保护,可以被恶意攻击者修改。
- Partial RelRO:只读保护了重定位表的一部分,但是还存在一部分是可写的。
- Full RelRO:完全只读保护了重定位表,使得攻击者无法修改其中的地址。
Stack(栈):指定二进制文件中的栈保护,包括以下两个方面:
- Canary:栈中插入了一个随机值作为标记,用于检测缓冲区溢出等攻击。
- NX:栈不可执行,防止栈上的数据被当做代码执行。
NX(Non-eXecutable, 不可执行):指定二进制文件数据段(Data Segment)的可执行性。NX可以防止攻击者执行已经存在于二进制文件中的恶意代码。
PIE(Position Independent Executable, 位置独立执行):指定二进制文件是否启用了地址随机化。启用PIE后,二进制文件会在每次运行时随机选择一个内存位置,使得攻击者很难通过静态分析获取二进制文件的内存地址,从而增加攻击的难度。
python pwn
模块的使用如:
1 |
|
payload1
中是生成的一段二进制数据, 可用于发送到服务器.
关于 IDA Pro (Interactive DisAssembler Professional)
- 最主要的静态反汇编/反编译工具
- 提供强大的逆向功能
IDA-view 一般是主界面, 其有两种界面, 一种是 graph, 一种是纯文本, 可以用右键切换.
按 F5 可以反编译, 从汇编中提取出 C 源程序逻辑.
Hex view 中可以查看二进制源码.
Imports 是导入表, 其包含一些导入的符号信息.
Exports 是导出符号表, 有一些导出符号信息.
Strings window 上有连续字符串的地址信息.
最左边是一个函数栏, 包含定义的函数信息. (一般用得比较少)
界面如:
在顶部 View -> Open subview -> Segment registers
比较有用, 可以查看段表.
关于 gdb 以及其 pwndbg (pwn debug) 插件
其为动态调试. 利用其汇编级别的调试.
常见指令:
汇编级别的调试指令如 si
(step instruction)
在指定地址下断点: b *0x565561c0
其他的 pwn 工具
- LibcSearcher
- one_gadget
- ropper
- patchif
注意修改 glibc 的地址.
关于 stack overflow
有栈上溢, 也有栈下溢.
一个游戏 SHENZHEN I/O
栈空间中一般放调用相关的内容.
如果要看栈的具体分段, 可以用 gdb
的 vmmap
命令查看.
栈的大致内容:
一个栈帧主要包括的内容有:
- 返回地址, 参数
- 寄存器值
- 局部变量
注意上一个栈帧的 bp
的值.
一般一个栈帧的顶部和底部的数据:
- 底部, 存放上一个栈帧的
bp
的值 - 顶部, 存放想要跳转的地址的值
return address
8 Pwn 程序装载细节与栈帧结构
栈顶就是当前栈帧的顶部.
ELF 文件类型:
- Relocatable File (
*.o
), 可重定位文件, 用于链接 - Executable File (
*
), 可执行文件 - Shared Object File (
*.so
), 共享目标文件, 用于动态链接
ELF 文件结构:
Sections (节视图) 存储在磁盘中, Segments (段视图) 存储在内存中.
不同的 Sections 根据它们对权限的不同要求被装载进不同的 Segments 中.
虚拟地址空间方便不连续地分配内存.
函数调用约定
形如 __cdecl
, __stdcall
, __fastcall
.
__cdecl
要求调用这清除栈帧. 即 push
多少个进来, 就 pop
多少个出去. 也就允许了可变参数列表的存在. (注意这里是 调用者 )
9 Web php入门及序列化与反序列化
PHP 的全称为 Pre Hypertext Processor. 其可以嵌入到 HTML 中.
如:
1 |
|
其功能包括:
- 生成动态页面内容
- 创建, 打开, 读取, 写入, 关闭服务器上的文件
- 收集表单数据
- 发送和接收
cookies
- 添加, 删除, 修改数据库中的数据
- 限制用户访问网站上的一些页面
- 加密数据
等.
搭建 php 环境
安装一些 Web 服务器软件, 如 Apache
, Nginx
, Tomcat
, IIS
等.
配置服务器和防火墙.
也可参考 WAMP(Windows Apache MySQL PHP)/LAMP(Linux Apache MySQL PHPj) 等.
基础语法
- 可放在文档中的任意位置
- 以
<?php
开始, 以?>
结束, 如:1
2
3
4
5<?php
// $str = 'Hello World';
// echo $str;
// php codes
?> - PHP 文件通常包含 HTML 标签和一些 PHP 脚本代码
一个 index.php
的示例:
1 |
|
php 的变量以 $
开头.
有时题目禁掉了 A-z
和 0-9
的变量名, 可以用 _
下划线作为变量名使用.
变量的作用域
local
global
1
2
3
4
5
6
7
8
9<?php
$x = 3;
$y = 5;
function myFunction() {
global $x, $y; // use the outer variable
$z = $x + $y;
echo $z;
}
?>static
1
2
3
4
5
6
7
8
9
10
11<?php
function testStatic() {
static $val = 1;
echo $val;
$val++
}
testStatic();
testStatic();
testStatic();
?>parameter
echo
和
echo
可以输出一个或多个字符串print
只允许输出一个字符串, 返回值总是 1
1 |
|
一种输出多行字符串的方式
1 |
|
两种比较
- 松散比较, 用
==
进行比较, 只比较值, 不比较类型 - 严格比较, 用
===
进行比较, 比较值的同时也比较类型
序列化和反序列化
serialize()
将一个对象转换成一个字符串.
unserialize()
将字符串还原为一个对象.
在 PHP 应用中, 序列化和反序列化一般用于做缓存, 如 session
, cookie
缓存.
序列化时常用的方法:
就是定义这些方法后, 会在特定的场景中被自动调用.
构造 POP (Property-Oriented Programing) 链
利用原有的代码 (控制代码流程) 拼凑出自己需要的功能.
利用 Autoloading
采用 Autoloading
来自动加载文件列表, 自动加载一些类, 如:
1 |
|
可通过此机制上传自己的 php
文件.
在 php 版本 7.2.0
之后, 使用 __autoload()
会 warning, 取而代之的是 sql_autoload_register()
.
库的安装
PHP 用 Composer 来管理依赖.
其默认从 Packagist 下载依赖库.
漏洞利用思路:
- 从可能存在漏洞的依赖库文件入手
- 从应用的代码框架的逻辑上入手
- 从 PHP 语言本身漏洞入手
构造漏洞的常见方法:
- 在依赖库中全局搜索
__wakeup()
和__destruct()
- 查看
composer.json
文件, 其标明了应用需要的库, 可用来寻找 POP 组建 - 查找流行库的类, 查看是否存在可以利用的组件
- 手动验证, 构建 POP 链, 利用易受攻击的方式部署应用程序和 POP 组件, 通过自动加载类来生成 poc 及测试漏洞.
一个示例
__toString
触发漏洞.
10 Pwn 栈溢出实战基础
X86 架构常见的调用约定:
- stdcall
- cdecl
- fastcall
(前两者把参数压入栈中进行传输, 后者压入寄存器)
cdecl 调用约定的流程 (32 位)
- 参数全部存储在栈中
- 参数自右向左入栈
- 由调用者清理栈区 (参数部分)
eax
保存函数返回值
64 位的有一些区别, 前六个参数存储在寄存器中. 从第一个到第六个分别存储在: rdi
, rsi
, rdx
, rcx
, r8
, r9
这里也要关注传入参数的大小, 4 字节存储在如 edi
中, 而 8 字节才是 rdi
中.
其余的参数仍然存放在栈中, 还是从右向左传. 如:
如:
(这里的栈帧是 32 位的)
局部变量在自己当前的栈帧中, 而传入的参数在其他栈帧 (如上一个栈帧) 中.
leave
指令分为两个步骤:
esp = ebp
, 也就是删除栈帧pop ebp
, 获取上一个栈帧的基址
区分 lea
(Load Effective Address) 指令: 其作用是将一个有效地址(Effective Address)加载到指定的寄存器中,而不是加载内存中的数据. 如:
1 |
|
这里的ESI寄存器包含数组的基地址,20是第5个元素的偏移量。因此,LEA指令计算出该元素的地址,并将该地址加载到EBX寄存器中。
注意 , 栈帧的开辟操作存在于函数调用时, 每当一个函数被调用时,都会创建一个新的栈帧来存储该函数的局部变量、函数参数和其他执行上下文信息。这个过程被称为栈帧开辟(stack frame setup).
栈溢出
栈溢出 (stack overflow) 属于缓冲出溢出 (buffer overflow) 的一个大类, 还有:
- Heap Overflow
- Others
缓冲区溢出的定义: 编写程序时没有考虑或错误设置用户输入长度, 导致用户向缓冲区输入长度超过接收变量长度, 从而覆盖到其他正常数据, 破坏栈帧结构.
造成缓冲区溢出的主要函数有:
gets
, 读到换行为止, 没有长度限制read
scanf
如:
- Read 读取长度超过 buf 长度:
1
2
3
4void foo() {
char s[0x10];
read(STDIN_FILENO, s, 0x30);
}
示例 python 文件:
1 |
|
可以在符号表中找对应的符号然后获取其地址.
Shellcode
Shellcode 指能使程序调用 shell 的一段代码(通常为汇编级别/机器码), 一旦某种 Shellcode 被执行, 就能够拿到目标机器的控制权限, 从而获取 flag.
一般 Shellcode 不会特别长, 且运行 Shellcode 的环境一般不具有运行外部库函数的能力.
如:
execve("/bin/sh", 0, 0)
- 触发中断
int 0x80
/ syscall, 此时需要在中断之前设置一些条件, 如要调用execve
, 需要设置一些寄存器值:
示例汇编:
1 |
|
漏洞特征:
- 有一个可读可写可执行段, 这个漏洞在
checksec
扫描器中会显示为RWX segments
. - 调用某处可控内存空间代码
利用方式:
- 在可控空间处注入 Shellcode
- 使程序执行流转向该处
11 Web sql 注入基础
SQL, Structured Query Language, 用于存储数据以及查询, 更新和管理 关系数据库.
关系数据库指数据库中的每一个元素都可以用一列和一行进行定位.
SQL 是解释型语言 (用解释器, 而不是编译器)
SQL 注入指用户插入了额外的 SQL 语句来执行, 实现无账号登录, 篡改数据库等攻击.
MySQL 本身实际上只是一个 SQL 接口, 它的内部还包含了多种数据引擎, 常用的包括 InnoDB
, MyISAM
.
一些绕过手法, 比如 &
, ||
, ^
被禁掉了, 可以尝试 and
, or
, xor
.
一些词被禁掉了, 可以尝试用 ascii()
等编码函数, 传送字母后拼接.
常用 SQL 语句和操作:
注意不同的 sql 版本有不同的关键字.
注入条件:
- 用户可控输入 (要有对话框让用户输入)
- 输入内容与数据库交互 (输入的内容)
简单的示例 – 闭合 (即利用双引号, 单引号的闭合构造一直为真的语句, 主要是为了让自己能够操作后端语句), 如:
1 |
|
构造如 select * from data where id = 1 or 1 = 1
, 就能输出所有数据.
有时常见的注释符, 以及单引号会被 ban 掉, 此时可用的注释符有:
%23
--+
;%00
- `
示例:
1 |
|
在这个语句中, $username
和 $password
变量两侧都有一对单引号, 此时如果输入 \
, 则可以转义一个单引号, 如:
1 |
|
此时第一个闭合为 and password =
这个值, 最后一个单引号多出来了, 不处理则会报错, 此时用 ;%00
来把这个单引号注释掉, 并在前面加上自己的操作:
1 |
|
此时 or 1 = 1
则可以运行.
联合查询注入 –
Order by
探测注入点
利用 union
联合查询, 注意 联合查询 的基本要求:
- 查询列数必须一致
- 查询语句的查询的各列类型, 顺序最好一致
联合查询的步骤:
- 闭合
- 判断字段数 (用
Order by
)
如:
1 |
|
接着试:
1 |
|
直到报错, 就能知道有多少行数据.
一般情况下, 会限制返回数据的行数 (用了 limit
语句等). 此时需要判断结果输出的位置, 比如原本只输出第一行, 得知之后, 就可以让需要的数据输出在第一行.
information_schema
这个数据库中的 TABLE
和 COLUMNS
表中分别提供了数据库中的表的信息和列的信息.
通过联合查询注入爆库, 爆表, 爆列的方法:
爆库名:
union select 1,2,database()
爆表名:
union select 1,2,group_concat(table_name) from information_schema.tables where table_schema = database()
union select 1,2,group_concat(table_name) from information_schema.columns where table_schema = database()
爆 Column 名:
union select 1,2,group_concat(column_name) from information_schema.columns where table_name = 'users'
group_concat
函数将所有数据连接为一条数据, 从而可以显示出来.
一个用 php 进行攻击的示例:
1 |
|
使用工具探测 sql 注入漏洞
使用 sqlmap
, 可用 pip
下载.
常用操作:
12 Pwn Ret2xx & ROP链构造
介绍可执行文件的保护措施 – NX.
NX, No Execute bit (禁止执行位), 是应用在 CPU 上的安全技术, 其支持了操作系统级别的 DEP (Data Execution Prevention).
在应用了 NX 的系统上 (如果可执行文件开启保护), 会把内存中的区域分类为只供存储指令和只供存储数据两种, NX bit 被标记在内存分页使用的页表索引上, 如果置 1, 则该页内存数据不允许被执行, 即把所有内容作为数据处理.
其杜绝了一部分缓冲区攻击.
同样可以用 checksec 工具来查看保护措施.
ROP 介绍
ROP (Return-Oriented Programming), 允许攻击者在开启了栈不可执行等安全保护技术的情况下, 执行恶意代码.
核心思想是通过栈溢出等方式, 改写栈上的控制信息, 以控制调用栈, 劫持程序控制流并执行一些针对性的命令序列 (gadgets, 指一些以 ret 结尾的小段汇编指令, 他们的执行通过 ret 语句和栈上控制的地址相连, 构成一条 ROP 链, 链的功能是设置寄存器值, 泄露信息, 调用函数等)
Ret2xx, 指构造 ROP 链的几种方法
Ret2Text
, 一般条件是, gadget 存在于 Text 中, 或存在于 ELF 文件中, 其同样通过将返回地址改写为能执行某些特定功能的 gadget 地址来构造 ROP 链.
可以用 ropper
和 pwntools
(ELF class) 工具辅助, 用于查找 ELF 文件中的 gadgets.
在 64 位中, 由于 rdi
是传入的第一个参数, 因此将 rdi
的值设置为 /bin/sh
.
对于某些 ELF 文件不含有 backdoor
函数的情况, 此时需要借助 PLT
表 (前提是这个 ELF 文件引用过 system
函数, 此时 PLT
表中才存在这个条目)
如:
1 |
|
Ret2syscall
, 一般条件是, 存在静态链接的可执行文件, 因此指令非常多, 也提供了许多 gadget.
最核心的 gadget 为 syscall(int 0x80)
(没有 system
函数可调用时就利用系统中断)
一般可以用 file
命令查看一个文件是否有静态链接 (statically linked)cnss{Welc0me_to_SA_W0r1d}.
可以利用的辅助工具: ROPgadget
.
最常见的为 Ret2libc
。
PLT 表和 GOT 表介绍
- GOT,
Global Offset Table
, 全局偏移表, 包含所有需要动态链接的外部函数的地址 (在第一次执行后) - PLT,
Procedure Link Table
, 过程链接表, 包含调用外部函数的跳转指令 (跳转到 GOT 表中), 以及初始化外部调用指令
Linux 虚拟内存分段映射中, 一般会分出三个相关的段:
.plt
, 放跳转指令.got.plt
.got
, 放地址
调用一个函数, 就需要跳转, 因此需要 plt
表中的值, 同时需要函数的地址, 因此也需要 got
表中的值.
若需要中找到一个 system
函数在内存中的地址, 需要知道两个东西:
system
函数在glibc
中的地址glibc
被链接到内存空间中的基址, 一般未知, 需要手段来泄漏.
13 Web XSS 入门
XSS, Cross Site Scripting, 向网页中注入恶意代码, 导致用户浏览器在加载网页, 渲染 HTML 文档时就会执行攻击者的代码.
按照漏洞成因, 一般分类为:
- 反射型
- 存储型
- DOM 型
或按照输出点位置分为:
- 输出在 HTML 属性中
- 输出在 CSS 代码中
- 输出在 JavaScript 代码中
反射型 XSS
原型如:
1 |
|
攻击者提交如:
1 |
|
则服务端解析后会通过 echo
语句将攻击者输入的内容完整的输出到 HTTP 响应, 从而导致弹窗.
储存型 XSS
也就是说用户点击页面上的链接而遇害 (链接即 JavaScript 代码, 存储在服务器中)
DOM 型 XSS
其不需要服务器解析, 而通过浏览器解析.
输出在 HTML 属性中
这个和 SQL 注入类似:
输出在 CSS 代码中
输出在 JavaScript 代码中
Payload 和 Bypass
特定标签过滤, 事件过滤
绕过方法如:
- 用 JavaScript 伪协议
- HTML5 新属性
- 对事件属性进行 Fuzz 测试
铭感关键字过滤
绕过方法如:
- 字符串拼接
- 编码
- svg 标签
- 提交绕过
DVWA 平台默认账号 admin
, 默认密码 password
14 Pwn ELF保护机制与绕过、高级栈溢出技巧
ASLR 介绍
ASLR, Address Space Layout Randomization, 地址空间配置随机化.
将可执行文件, 共享库, 栈, 堆的基址随机化. 用于防范明确地址的内存破坏攻击.
可以用 地址泄漏 来应对.
该保护措施由操作系统配置为开或者不开, 而不是写在 ELF 文件头.
PIE 介绍
PIE, Position-independent executable (地址无关代码/可执行文件), 无论文件被加载进内存空间的什么地址中, 程序都能正常运行. (程序中不会写死地址, 是动态的)
就像共享库文件动态链接到内存中, PIE 使其能正确处理外部引用.
其特点是, 整个 ELF 文件都会被装载进一个 随机偏移 的连续的内存空间里, 只是 基址 变成了未知的, 其他如偏移都是相同的.