CSAW 2018 writeup
前言
很久沒活動了,web能力顯著下降。。。
WEB
Ldab
dab http://web.chal.csaw.io:8080
一直沒看懂這道題的提示,開啟頁面後右上角出現:
然後有一頁人員名單, Here is a list of all users and groups
。
嘗試了一波注入後並沒有發現突破點,後來經人提示才明白這是 LDAP/">LDAP注入
。
關於LDAP:
輕量級目錄訪問協議(LDAP)用於儲存有關使用者,主機和許多其他物件的資訊。LDAP注入是伺服器端攻擊,可以允許公開,修改或插入有關LDAP結構中表示的使用者和主機的敏感資訊。這是通過操作輸入引數然後傳遞給內部搜尋,新增和修改函式來完成的。 Web應用程式可以使用LDAP,以便使用者在公司結構中對其他使用者的資訊進行身份驗證或搜尋。LDAP注入攻擊的目標是在將由應用程式執行的查詢中注入LDAP搜尋過濾器元字元。
LDAP的語法如下:
Metachar | Meaning |
---|---|
& | Boolean AND |
| | Boolean OR |
! | Boolean NOT |
= | Equals |
~= | Approx |
= | Greater than |
<= | Less than |
* | Any character |
() | Grouping parenthesis |
所以LDAP表達 and
需要用 (&(condition1)(condition2))
, or
類似。同時它還支援正則語法主要是: .
、 *
。
一些例子:
如果我們想要在上面的LDAP結構中查詢名為“ steve ”的人,我們的查詢將如下所示: (cn=steve) 也許我們想要搜尋名稱以“ s ” 開頭的任何成員,那麼我們可以使用萬用字元: (cn=s*) 我們還可以使用“ | ”運算子搜尋名稱以“s”或“t”開頭的任何人: (|(cn=s*)(cn=t*))
題目中預設是 GivenName
,如:
所以一般來說我們的flag應該就藏在某一項中,我們找一下 LDAP attributes
,看看其他欄位中是否有flag。LDAP屬性欄位可以在 這裡找到 。但都沒有發現。使用 *)(|(uid=*)
列出所有項也還是沒有出來。但可以確定的是flag就在這些項中,所以繼續嘗試,直到找到這個:
*)(uid=*))(|(uid=*
得到flag:
參考連結
https://www.owasp.org/index.php/Testing_for_LDAP_Injection_(OTG-INPVAL-006)
http://www.4hou.com/technology/9090.html
https://www.anquanke.com/post/id/159378
MISC
bin_t
Binary trees let you do some interesting things. Can you balance a tree? nc misc.chal.csaw.io 9001 Equal nodes should be inserted to the right of the parent node. You should balance the tree as you add nodes.
如題,nc上去會給出一組數字,而你的任務就是將這組數字轉換成 平衡二叉樹
,並且給出該ACL樹的前序遍歷,下面直接給程式碼:
//AVL(自動平衡二叉樹) #include <stdio.h> #include <stdlib.h> typedef int ElemType; //每個結點的平均值 typedef enum { EH = 0, LH = 1, RH = -1 }bh_t; typedef enum { FALSE = 0, TRUE = 1 }bool_t; //定義平衡二叉樹 typedef struct BSTNode { ElemType key;//平衡值 int bf; struct BSTNode *lchild,*rchild; }BSTNode, *BSTree; //中序遍歷 void InOrderTraverse(BSTree root) { if(NULL != root) { InOrderTraverse(root->lchild); printf("%d\t",root->key); InOrderTraverse(root->rchild); } } //前序遍歷 void PreOrderTraverse(BSTree root) { if(NULL != root) { printf("%d,",root->key); PreOrderTraverse(root->lchild); PreOrderTraverse(root->rchild); } } //單向右旋轉 void R_Rotate(BSTree *p) { BSTree lc=(*p)->lchild; (*p)->lchild=lc->rchild; lc->rchild=*p; *p=lc; } //單向左旋轉 void L_Rotate(BSTree *p) { BSTree rc=(*p)->rchild; (*p)->rchild=rc->lchild; rc->lchild=*p; *p=rc; } //先左旋後右旋平衡旋轉 void LeftBalance(BSTree *T) { BSTree lc=(*T)->lchild; BSTree rd = lc->rchild; //判斷進行向哪邊旋轉 switch(lc->bf) { case LH: (*T)->bf=lc->bf=EH; R_Rotate(T); break; case RH: switch(rd->bf) { case LH: (*T)->bf=RH; lc->bf=EH; break; case EH: (*T)->bf=lc->bf=EH; break; case RH: (*T)->bf=EH; lc->bf=LH; break; } rd->bf=EH; L_Rotate(&((*T)->lchild)); R_Rotate(T); break; } } //先右旋後左旋平衡旋轉 void RightBalance(BSTree *T) { BSTree rc=(*T)->rchild; BSTree ld=rc->lchild; switch(rc->bf) { case RH: (*T)->bf=rc->bf=EH; L_Rotate(T); break; case LH: switch(ld->bf) { case RH: (*T)->bf=LH; rc->bf=EH; break; case EH: (*T)->bf=rc->bf=EH; break; case LH: (*T)->bf=EH; rc->bf=RH; break; } ld->bf=EH; R_Rotate(&((*T)->rchild)); L_Rotate(T); break; } } //插入元素 bool_t InsertAVL(BSTree *t,ElemType e,bool_t *taller) { if(NULL == t) return FALSE; if(NULL == *t) { *t=(BSTree)malloc(sizeof(BSTNode)); if(NULL == *t) return FALSE; (*t)->key=e; (*t)->lchild=(*t)->rchild=NULL; (*t)->bf=EH; *taller=TRUE; } else { if(e==(*t)->key) { *taller=FALSE; return FALSE; } if(e<(*t)->key) { if(FALSE == InsertAVL(&((*t)->lchild),e,taller)) return FALSE; if(*taller) { switch((*t)->bf) { case LH: LeftBalance(t); *taller=FALSE; break; case EH: (*t)->bf=LH; *taller=TRUE; break; case RH: (*t)->bf=EH; *taller=FALSE; break; } } } else { if(FALSE == InsertAVL(&((*t)->rchild),e,taller)) return FALSE; if(*taller) { switch((*t)->bf) { case RH: RightBalance(t); *taller=FALSE; break; case EH: (*t)->bf=RH; *taller=TRUE; break; case LH: (*t)->bf=EH; *taller=FALSE; break; } } } } return TRUE; } static void destroy(BSTree *t) { if(NULL != *t) { destroy(&((*t)->lchild)); destroy(&((*t)->rchild)); free(*t); *t = NULL; } return; } void destroyAVL(BSTree root) { if(NULL != root) { destroy(&root); } return; } int main() { BSTree root=NULL,r; bool_t taller=FALSE; int array[]={16,67,87,73,81,71,11,38,49,59,30,85,73,26,58,35,50,72,70,50,76,17,94,8,58,55,68,88,15,37,75,27,75,30,1,85,94,27,7,71,8,9,70,4,17,21,38,29,2,71,85,5,59,67,83,93,47,5,81,29,64,36,15,89,45,90,40,21,53,51,35,57,5,11,48,23,44,66,71,81,95,73,17,42,1,98,19,34,6,87,68,21,78,88,78,17,97,49,90,5}; int i = 0; for(i=0; i < 100; i++) InsertAVL(&root,array[i],&taller); printf("中序遍歷:\n"); InOrderTraverse(root); printf("\n先序遍歷\n"); PreOrderTraverse(root); destroyAVL(root); root = NULL; return 0; }
Algebra
Are you a real math wiz? nc misc.chal.csaw.io 9002
nc上去發現是解一元方程。
寫一個求解器:
# -*- coding:utf-8 -*- import socket sc = socket.socket()# 建立 socket 物件 host = "misc.chal.csaw.io"# 獲取本地主機名 port = 9002# 設定埠 addr = (host, port) sc.connect(addr)# 繫結埠號 print sc.recv(1024) def solve1(eq,var='X'): eq1 = eq.replace("=","-(") + ")" c = eval(eq1,{var:1j}) if (-c.real == 0): return 0 else: return -c.real/c.imag def find(): data = sc.recv(1024) print data equation = data.split('\n')[0] result = str(solve1(equation)) print '正在求解:' + equation + ', result = ' + result sc.send(result + '\n') print '===========================' find() i = 1 while True: print '============ 第 ' + str(i + 1) + ' 輪 ===============' data = sc.recv(2048) print data equation = data.split('\n')[1] result = str(solve1(equation)) print '正在求解:' + equation + ', result = ' + result sc.send(result + '\n') i += 1
Crypto
flatcrypt
no logos or branding for this bug Take your pick nc crypto.chal.csaw.io 8040 nc crypto.chal.csaw.io 8041 nc crypto.chal.csaw.io 8042 nc crypto.chal.csaw.io 8043 flag is not in flag format. flag is PROBLEM_KEY
答題附件:
# -*- coding: utf-8 -*- import zlib import os from Crypto.Cipher import AES from Crypto.Util import Counter ENCRYPT_KEY = bytes.fromhex('0000000000000000000000000000000000000000000000000000000000000000') # Determine this key. # Character set: lowercase letters and underscore PROBLEM_KEY = 'not_flag' def encrypt(data, ctr): return AES.new(ENCRYPT_KEY, AES.MODE_CTR, counter=ctr).encrypt(zlib.compress(data)) while True: f = input("Encrypting service\n") if len(f) < 20: continue enc = encrypt(bytes((PROBLEM_KEY + f).encode('utf-8')), Counter.new(64, prefix=os.urandom(8))) print("%s%s" %(enc, chr(len(enc))))
這道題網上搜了一下發現有類似的題目。
http://www.blue-lotus.net/plaidctf-2013-crypto-compression250-writeup/
https://systemoverlord.com/2013/04/30/plaidctf-compression/
https://www.cnblogs.com/shuidao/p/3151067.html
解題的關鍵是利用 zlib.compress()
壓縮資料時造成的漏洞,當壓縮的字串中有 3個以上
重複的字串時, LZ77
壓縮演算法就會對字串進行壓縮。如:
vvvvv Blah blah blah blah blah! ^^^^^ 接下來的5個字元正好和已經在資料流中的字串相等, 而且剛好在當前資料點的前5個字元開始. 在這個case中,我們可以在資料流中輸出特殊的字元, 一個長度數字 和一個距離數字. 目前資料是: Blah blah b 壓縮後的格式是: Blah b[D=5,L=5]
再加上AES使用CTR流密碼模式,那麼待加密字元的長度不一樣就會產生 不一樣長度
的密文。這個可以用下面這個指令碼體驗一下:
import zlib import os import string import random from Crypto.Cipher import AES from Crypto.Util import Counter ENCRYPT_KEY = bytes.fromhex('0000000000000000000000000000000000000000000000000000000000000000') # Determine this key. # Character set: lowercase letters and underscore PROBLEM_KEY = 'crime_doesnt_have_a_logo' padding = 'A' * 20 def encrypt(data, ctr): return AES.new(ENCRYPT_KEY, AES.MODE_CTR, counter=ctr).encrypt(zlib.compress(data)) for i in range(30): f = padding + ''.join(random.sample(string.ascii_letters + string.digits, 4)) # if len(f) < 20: #continue enc = encrypt(bytes((PROBLEM_KEY + f).encode('utf-8')), Counter.new(64, prefix=os.urandom(8))) print("%s --> %s" %(f, len(enc))) print('---------finally----------') enc = encrypt(bytes((PROBLEM_KEY + padding + 'crime').encode('utf-8')), Counter.new(64, prefix=os.urandom(8))) print("%s --> %s" %('crime', len(enc)))
所以可以看出當我們能得到一個三個字元都一樣的字串時我們得到的長度就會 變小
(壓縮),然後根據這三個字元不斷的 往前猜和往後猜
就能找到所有的字元。
最終exp:
# -*- coding: utf-8 -*- import socket import random, string sc = socket.socket()# 建立 socket 物件 host = "crypto.chal.csaw.io"# 獲取本地主機名 port = 8040# 設定埠 addr = (host, port) sc.connect(addr)# 繫結埠號 strs = 'abcdefghijklmnopqrstuvwxyz_' size = 20 print sc.recv(1024) flag = '' padding = ''.join(random.sample(string.ascii_uppercase + string.digits + string.ascii_letters, size)) print '[!] padding: ' + padding def get_fore(): last_len = 0 last_tmp = '' for f1 in strs: for f2 in strs: for f3 in strs: for f4 in strs: tmp = f1 + f2 + f3 + f4 if tmp == 'aaaa': continue data = padding + tmp sc.send(data + '\n') temp = sc.recv(1024).split('\n')[0] # print '[+] recv data: ' + temp try: len = ord(temp[-1:]) except: print '[ERROR] error...' print '[!] padding: ' + padding print '[!] recv data: ' + temp continue if last_len != 0 and last_len != len and tmp not in padding and last_tmp not in padding: # 找到一個 if last_len > len: flag = tmp else: flag = last_tmp print '[*] some flag: ' + flag sc.close() exit(0) last_len = len print '%s --> %s' % (tmp, str(len)) last_tmp = tmp def get_more(target): i = 1 over = False while not over: last_len = 0 last_tmp = '' for f in strs: # data = padding + target[i:] + f# 向後 data = padding + f + target[:len(target) - i]# 向前 sc.send(data + '\n') temp = sc.recv(1024).split('\n')[0] print '[+] recv data: ' + temp lens = ord(temp[-1:]) print 'sended: %s --> %s' % (data, str(lens)) if last_len != 0 and last_len != lens: # 找到一個 if last_len > lens: # target += f# 向後 target = f + target else: # target += last_tmp# 向後 target = last_tmp + target print '[*] target: ' + target break last_len = lens last_tmp = f if f == '_': over = True print '[!] OVER!' i += 1 get_fore()# 將得到的字元放入下個函式的引數中 # get_more('aave')
先執行 get_fore()
得到部分flag,然後代入 get_more()
,在該函式裡按照向前、向後的註釋執行,最終得到flag: rime_doesnt_have_a_logo
。但是提交發現不對,然後隊裡的pwn大佬提出試一下前面加個 c
(’crime –> 罪惡’),即 crime_doesnt_have_a_logo
,bingo!
Forensics
simple_recovery
Simple Recovery Try to recover the data from these RAID 5 images!
給了兩個映象檔案,file了一下:
disk.img0: Hitachi SH big-endian COFF object, not stripped
參考連結:https://forensic.n0fate.com/2010/05/29/raid-system-forensics/
發現是要從兩個raid5的映象檔案中恢復完整映象,找到個破解版工具(建議虛擬機器下使用):
連結:https://pan.baidu.com/s/1lFsZ27h1rhZ_kEbglO4yBg 密碼:o0ps
最後可以得到一個完整的映象檔案,找到flag:
flag{dis_week_evry_week_dnt_be_securty_weak}
:panda_face: Rewind
Sometimes you have to look back and replay what has been done right and wrong
壓縮包解開後有一個映象檔案和一份日誌,file一下發現:
rewind-rr-snp: QEMU suspend to disk image
發現是QEMU image,先是用010找了一下。
發現一大堆flag,但是我試到第三的時候flag就對了。。。
flag:flag{RUN_R3C0RD_ANA1YZ3_R3P3AT}