2019/9/30 更新:三星題目已解出來會在最下面做說明
終於進入到General Skill的最後一篇了,這裡的題目都不難,主要是介紹、應用一些工具,接下來就會針對其他領域來解題了
這題給了一段C的程式碼
應該不難看出他開啟了兩個檔案,一個是存Flag的檔案,另一個檔案要跟他定義的yes字串做比較,若一樣則輸出flag,這題很直覺的會想要建一個permission.txt的檔案,但是是沒有辦法在這個資料夾下創建這個檔案,再回去看看程式碼有個很關鍵的地方,flag.txt是用絕對路徑開啟,permission是用相對路徑去開啟,這時我們回到家目錄下 cd ~
在這裡就可以建立一個permission的檔案
使用echo 'yes' > permission.txt
,然後在使用絕對路徑的方式執行absolutely-relative檔案,這時當前的路徑是在 ~所以可以執行到permission.txt 於是Flag就到手囉
~$ echo 'yes' > permission.txt
~$/problems/absolutelyrelative_4_bef88c36784b44d2585bb4d2dbe074bd/absolutely-relative
You have the write permissions.
picoCTF{3v3r1ng_1$_r3l3t1v3_3b69633f}
這題是要我們用gdb這個工具來找到藏在程式中的Flag,題目給了一個程式,先把他執行看看
什麼都沒有發生,不過大概可以看出有flag_buf這個變數是關鍵,就直接開gdb吧gdb ./run
我們可以用disass main
先看main的組合語言程式碼,這裡看不懂組合語言沒有關係,這邊很明顯的看出做了四次call,大概可以猜做完decrypt_flag就可以找到flag了,所以我們再看看decrypt_flag這裡的組合語言
這裡就不管他內部怎麼做,直接跳到最後面找到leaveq
這個指令,這裡可以先當作這個函式已經做完了,所以把這個指令的記憶體位址設為中斷點,等等run程式就會卡在這,我們在從這裡看flag_buf這個變數的值
b *xxx
這個指令是設中斷點的意思r
就是執行的意思x/s flag_buf
這是將變數以字串的形式顯示出來
gdb也是一個很好用的工具,像是以後會說到的逆向工程(reverse)也會用到,這裡只簡單介紹一下gdb的使用,這邊有一個網站有介紹一些gdb的指令給大家參考 https://kapeli.com/cheat_sheets/GDB.docset/Contents/Resources/Documents/index
這題給了兩個檔案一個是原始碼一個是執行檔,直接看原始碼吧,這邊原始碼有點長所以只擷取部份的關鍵做說明
else if(menu == 2){
printf("Current Auctions\n");
printf("[1] I Can't Believe its not a Flag!\n");
printf("[2] Real Flag\n");
int auction_choice;
fflush(stdin);
scanf("%d", &auction_choice);
if(auction_choice == 1){
printf("Imitation Flags cost 1000 each, how many would you like?\n");
int number_flags = 0;
fflush(stdin);
scanf("%d", &number_flags);
if(number_flags > 0){
int total_cost = 0;
total_cost = 1000*number_flags;
printf("\nYour total cost is: %d\n", total_cost);
if(total_cost <= account_balance){
account_balance = account_balance - total_cost;
printf("\nYour new balance: %d\n\n", account_balance);
}
else
printf("Not enough funds\n");
}
}
else if(auction_choice == 2){
printf("A genuine Flag costs 100000 dollars, and we only have 1 in stock\n");
printf("Enter 1 to purchase");
int bid = 0;
fflush(stdin);
scanf("%d", &bid);
if(bid == 1){
if(account_balance > 100000)
printf("YOUR FLAG IS:\n");
else
printf("\nNot enough funds for transaction\n\n\n");
}
這裡可以看到金額大於100000才可以得到Flag,但是這個程式碼內並沒有增加金額的方式,不過這邊可以看到total_cost = 1000*number_flags;
且他沒有對total_cost最小值做判斷,這時候可以利用讓他溢位的方式,因為電腦是二補數的方式所以當溢位後以2進位來看最左邊的數字變成1,對電腦來說這就是一個負數,下面做個簡單的範例
二補數 | 十進位 |
---|---|
000 | 0 |
001 | +1 |
010 | +2 |
011 | +3 |
100 | -4 |
101 | -3 |
110 | -2 |
111 | -1 |
且程式有將我們輸入的值*1000所以只要我們輸入的值大於2147483就可以達到目的將值變成負的
這裡的運算就相當於扣掉一個負數,就等於做加法
可以看到我們現在變很有錢了(要是現實世界也能這麼容易該有多好
就可以得到Flag囉
General Skill中的三星題目已經解出來囉
這裡列出了這題的規則,可以看到主要是大的會吃小的,如果一樣大那就並存,所以只要抓住這個關鍵就很好解了
這邊附上python程式碼
#!/usr/bin/python
from pwn import *
r = remote('2018shell.picoctf.com',8672)
def level(arr):
maxLevel=0
count=0
for i in arr:
if i =='(':
count+=1
else:
maxLevel=max(maxLevel,count)
count-=1
return maxLevel
def combine(a,b):
return a+b
def absorb_left(a,b):
return '('+a+b[1:]
def absorb_right(a,b):
return a[:-1]+b+')'
def script_me(arr):
answer = arr[0]
for i in range(1,len(arr)):
answer_level=level(answer)
arr_level=level(arr[i])
if answer_level > arr_level:
answer=absorb_right(answer,arr[i])
elif answer_level < arr_level:
answer=absorb_left(answer,arr[i])
else:
answer=combine(answer,arr[i])
return answer
r.recvuntil('warmup.\n')
text = ''
while 'pico' not in text:
while '???' not in text and 'pico' not in text:
text=r.recvline()
if 'pico' in text:
print text
r.close()
text = text[:-7]
arr = text.split(' + ')
answer = script_me(arr)
r.sendline(answer)
這裡的三個function absorb-left
,absorb-right
,combine
就是剛剛說的三種規則,level
這個function計算大小以便後面判斷是大+小,小+大,
$ ./script_me.py
[+] Opening connection to 2018shell.picoctf.com on port 8672: Done
Congratulations, here's your flag: picoCTF{5cr1pt1nG_l1k3_4_pRo_0970eb2d}
接下來的主題會講Web,不過因為時間的關係實在沒有辦法把所有題目解完,所以會視情況做到一定的程度,因為希望在這次的鐵人賽可以講每個領域都介紹過一遍,若是有哪裡寫的不好或是有更好的方法也歡迎在下面留言討論~