1. 程式人生 > >hackme.inndy.tw的一些Writeup(5月30更新)

hackme.inndy.tw的一些Writeup(5月30更新)

hackme.inndy.tw的一些Writeup(6月3日更新)

推薦一下https://hackme.inndy.tw/scoreboard/,上邊有一些很好的針對新手的題目,但網上能搜到的Writeup很少,因此開了這篇博文記錄一下部分目前解出的題目(主要是pwn和re),以後會跟著解題進度的推進逐步更新,同時遵循inndy師傅的規矩,只放思路,不放flag。

pwn

0x00 catflag

送分題,nc連線,cat flag即可

0x01 homework

根據題目提示,是陣列越界的漏洞,第一次遇到這種漏洞覺得利用方式很巧妙。通過搜尋字串發現了get shell的函式call_me_maybe

, run_program函式中定義了一個大小為10的陣列,漏洞出現在edit number的選項裡,當我們輸入的index大於10時,程式是不會報錯的,而是會繼續朝著高地址edit資料,因此只需要edit run_program棧中的retcall_me_maybe, 這樣當我們退出run_program函式時,程式就會返回到call_me_maybe函式來get_shell,原理圖如下:

有兩點需要注意:

  1. 因為edit number中修改的資料是通過%d輸入的,因此需要以整形的方式輸入call_me_maybe函式的地址
  2. 修改ret的地址之後,還需要再輸入一次0來退出run_program
    的迴圈來出發ret

payload:

0x02 ROP

剛看這道題的時候覺得無從下手,程式中既沒有可以利用的函式,通過file命令檢視是靜態連結的也不能利用libc中的函式。後來注意到下一題提到了ROPgadget這個工具,才想到可以直接利用ROPgadget直接拼湊出ROP鏈,如下:

這樣就只需要我們通過緩衝區溢位的漏洞返回到ROPgadget構造的ropchain就可以了

payload:

0x03 ROP2

本來根據提示以為這道題需要自己拼湊出ropchain,但後來發現這一題中存在syscall這個可以實現系統呼叫的函式,如syscall(4, 1, &v4, 42)

即相當於write(1, &v4, 42)syscall(3, 0, &v1, 1024)即相當於read(0, &v1, 1024),syscall的第一個引數是系統函式的系統呼叫號,之後的引數依次為對應函式的引數,32位的系統呼叫號定義在/usr/include/x8664-linux-gnu/asm/unistd32.h中,可以看到execve的呼叫號為11,因此如果我們構造syscall(11, "/bin/sh", 0, 0)就相當於執行了execve("/bin/sh", 0, 0)即可get shell

介紹一個小trick,如下圖,可以看出v4是提示字串的開頭,但因為IDA沒有正確識別變數的型別,把提示字串分成了v4~v15多個變數,v4的地址為bp-0x33,v15為bp-0x9,因此字串長度為0x33 - 0x9 = 42

這時我們在v4上y一下,然後在彈出來的視窗上填入我們推斷出來的資料型別char v4[42],再點OK,這時IDA就能正確的識別出來v4的資料型別了,如下圖:

payload1:

通過兩次rop,實現get shell

payload2:

利用ROPgadget找到了一個pop4ret的gadget,利用pop4ret平衡堆疊,用一個ropchain實現getshell

需要注意,對於execve的第一個引數"/bin/sh",我們可以用先寫入一個固定地址(如bss段)的方式迂迴傳參。

0x04 toooomuch

放鬆心情的題,用IDA很容易可以得到通過驗證的passcode,然後用 二分/XJB猜 猜對數字後,就可以拿到flag

0x05 toooomuch-2

題目已經提示利用緩衝區溢位通過shellcode來get shell,IDA檢視程式流程,在gets函式中存在溢位

這樣只要把通過ROP把shellcode寫到bss段,再返回bss段執行shellcode即可

payload:

0x06 echo

看到echo,第一反應就是格式化字串的題,分析檔案後果然發現了很明顯的格式化字串漏洞

並且可以通過while迴圈多次利用,很經典的利用方式,先洩露出system_got中的system函式真實地址,再覆寫printf_got為system_addr,之後通過fgets讀入"/bin/sh"時,printf("/bin/sh")已經相當於system("/bin/sh"),即可get shell

payload:

需要注意如果leak的payload是p32(system_got) + "%7$s"的形式,那麼洩露出的system_addr是從第4位到第8位(p32(system_got))佔據了前4位,另外如果p32(system_got)中有\x00等bad char截斷printf的話,可以調整payload形式為%8$s + p32(system_got)

0x07 echo2

很明顯這一題也是格式化字串的漏洞,與上一題的不同在於:

  1. 64位,這意味著fmtstr_payload函式極有可能因為\x00不能用
  2. 開啟了PIE,也就是說got表等地址都是隨機的,但因為elf中各個部分的偏移是固定的,因此可以通過洩露elf基址的方法來確定其他部分的真實地址

通過除錯看出可以利用%41$p和%43$p洩露main地址與__libc_start_main的地址(0x23 + 6, 0x25 + 6):

洩露elf_base與libc_base的函式如下:

def getAddr():
    io.sendline("%41$p..%43$p..")
    elf_base = int(io.recvuntil("..", drop = True), 16) - 74 - 0x9b9#nm ./echo2
    libc_base = int(io.recvuntil("..", drop = True), 16) - 240 - libc_offset # this is for remote
    #  libc_base = int(io.recvuntil("..", drop = True), 16) - 241 - libc_offset #this is for local
    log.info("elf_base -> 0x%x" % elf_base)
    log.info("libc_base -> 0x%x" % libc_base)
    return elf_base + exit_got, libc_base + one_gadget

獲得elf_base與libc_base基址後,按照正常的格式化字串思路來就行,比如可以用one_gadget地址覆寫exit函式的got表地址.

0x08 echo3

這道題目做出來後在和站主交流時發現使用了非預期解,不太容易說清楚,這陣比較忙就先只放exp,忙過了這一陣寫一下對這道題的詳細分析(估計要幾個月後了),exp如下:

多說一句inndy人很nice

0x09 smash-the-stack

典型的canary leak,覆蓋__libc_argv[0]為flag在記憶體中地址,觸發__stack_chk_fail函式即可洩露flag

payload:

或者可以用更暴力的方法,不計算argv[0]到緩衝區的距離,用一個較大的值直接覆蓋過去即可:

payload:

需要注意的是write函式的長度是由使用者輸入決定的,給buf一個較小的值即可

0x0A onepunch

本來以為onepunch的意思是構造好payload一發get shell, 後來才發現是一個位元組一個位元組的打

題中有一個任意地址寫一個位元組的漏洞,剛開始覺得無從下手,後來除錯的時候發現程式碼段為rwxp許可權,才覺得柳暗花明。

既然可以在程式碼段任意地址寫,就意味著我們可以為所欲為的修改程式碼流程,因此就可以將

.text:0000000000400767 jnz short loc_400773

修改為

.text:0000000000400767 jnz 0x40071D

這樣就構成了一個迴圈,接下來在合適的位置寫shellcode,然後跳轉到shellcode即可

payload:

至於怎麼確定要patch的地址和值,我推薦keypatch,用keypatch修改完虛擬碼後,通過將  Options -> General ->Number of Opcode byte修改為非0值對比修改前後的位元組碼即可

0x0B rsbo

這道題的利用方法倒是第一次見到,對於read和write的第一個引數fd(檔案描述符),fd = 0時代表標準輸入stdin,1時代表標準輸出stdout,2時代表標準錯誤stderr,3~9則代表開啟的檔案。這一題的利用方式利用方式就是利用rop先用open函式開啟位於/home/rsbo/的flag,然後再用read(3, )把flag寫到一個固定地址上,最後用write輸出

payload:

0x0C leave_msg

經過分析程式碼的邏輯,繞過下邊的限制,就可以覆寫got表:

if ( v4 <= 64 && nptr != 45 )
  dword_804A060[v4] = (int)strdup(&buf);

而v4=atoi(&ntpr),經過搜尋atoi函式會跳過字串開頭的空白字元,因此只要構造(空格)-16就可以同時繞過上邊的兩個限制來覆寫puts的got表,然後再用\x00繞過strlen的限制就可以執行shellcode,payload如下:

此時的程式流程如下:

至於0x36是怎麼來的,我是除錯看出來的,如果哪位表哥有靜態計算的方法,還請不吝賜教!

0x0D stack

這個題開了全保護,第一眼看上去挺嚇人,但其實漏洞很容易發現,pop時並沒有對下標作出檢查,這就意味著我們可以通過一直pop利用陣列越界從棧上leak,先通過除錯看棧結構

可以看出,通過一直pop可以洩露_IO_2_1_stdout_的地址,進而確定libc的裝載基址和one_gadget地址

 1 def getBase():
 2     #  debug()
 3     for i in xrange(15):
 4         io.sendlineafter("Cmd >>", "p")
 5         io.recvuntil("-> ")
 6 
 7     libc_base = (int(io.recvuntil("\n", drop = True)) & 0xffffffff)- libc.symbols["_IO_2_1_stdout_"]
 8     info("libc_base -> 0x%x" % libc_base)
 9     one_gadget = libc_base + one_gadget_offset
10 
11     return one_gadget
12     #  return libc_base

通過main+27, main+427等地址可以洩露elf的裝載基址(當然並沒有用到),並且經過觀察,main+27, main+427分別是__x86_get_pc_thunk_dx和stack_push的返回地址,這樣我們只需要把main+427覆蓋為one_gadget的地址,再次呼叫stack_push時,就可以返回到one_gadget,進而get shell,有一個坑點是0xf段的地址會發生資料溢位,需要我們轉成int32的型別。

雖然聽起來很麻煩但只要耐心除錯並不難,這個題目的flag也說明了本題的重點就是leak from stack。

關於這個題有兩點值得一提:

  1. 剛開啟檔案使用F5時報錯

    很容易搜尋到這是因為函式的引數個數不匹配,我們定位到0x78D,發現540這個函式有一個引數,但IDA並沒有正確識別

    手動指定一個引數後(快捷鍵y指定型別),這個函式的報錯就解決了

    同樣的方法修復之後的報錯,就可以F5檢視虛擬碼了

    實現檢視虛擬碼後,可以通過檢視函式的具體實現進一步識別函式和修正函式引數

    如上圖的578函式,可以看出實際呼叫了hex(8128 + 56)這個地址,而這個地址在IDA中可以看出是scanf的地址,這樣我們就可以通過scanf的引數列表進一步修正引數了,附一張我修復之後的圖

    這樣在通過對比題目給出的原始碼,就很容易分析程式的功能了

    1. 這個題目開啟了PIE保護,利用pwn.gdb.attach除錯的時候和沒有開啟PIE保護的有些不同。

      以前我除錯開啟了PIE保護elf的方式是Uriel師傅教我的先找elf裝載基址

    from pwn import *
    import sys, os
    import re

    wordSz = 4
    hwordSz = 2
    bits = 32
    PIE = 0
    mypid=0
    context(arch='amd64', os='linux', log_level='debug')
      def leak(address, size):
         with open('/proc/%s/mem' % mypid) as mem:
            mem.seek(address)
            return mem.read(size)
    
      def findModuleBase(pid, mem):
         name = os.readlink('/proc/%s/exe' % pid)
         with open('/proc/%s/maps' % pid) as maps:
            for line in maps:
               if name in line:
                  addr = int(line.split('-')[0], 16)
                  mem.seek(addr)
                  if mem.read(4) == "\x7fELF":
                     bitFormat = u8(leak(addr + 4, 1))
                     if bitFormat == 2:
                        global wordSz
                        global hwordSz
                        global bits
                        wordSz = 8
                        hwordSz = 4
                        bits = 64
                     return addr
         log.failure("Module's base address not found.")
         sys.exit(1)
    
      def debug(addr = 0):
          global mypid
          mypid = proc.pidof(r)[0]
          raw_input('debug:')
          with open('/proc/%s/mem' % mypid) as mem:
              moduleBase = findModuleBase(mypid, mem)
              gdb.attach(r, "set follow-fork-mode parent\nb *" + hex(moduleBase+addr))    
View Code

但做這道題題時發現pwn.gdb.attach只有第一個引數是必須的

這樣就可以先進入gdb,通過vmmap找到elf基址後在下斷點進行除錯了

0x0E very_overflow

這個題目給了原始碼,分析起來方便了不少。這個題的漏洞也很容易發現,雖然申請了長度為128*(sizeof(buffer))的緩衝區,但可以無限的add_note,這就意味著我們可以先重複add_note耗盡緩衝區,然後繼續add_note和show_note時,就可以leak類似__libc_start_main這些資訊來確定libc裝載基址了,有了libc裝載基址後,通過rop構造system("/bin/sh")或者one_gadget都可以求解

至於add_note多少次,通過除錯可以很清楚的算出來

另外就是剛開始本地可以get shell,但遠端連線很容易超時,後來把context.log_level換成了info,減少了列印花費的時間,又把io.sendlineafter換成了直接io.sendline,就不容易超時了

0x0F tictactoe-1

給的elf檔案逆起來比較繁瑣,通過反編譯可以找到棋的原始碼,瞭解了程式的大體流程後,可以發現在落子時可以通過陣列越界覆寫GOT表

但因為程式有一個判負退出的功能,因此經過實驗最多隻能寫三個位元組,但對第一題而言,三個位元組已經足夠overwrite [email protected]為列印flag的地址

第一題很快就解決了,至於tictatoe-2,雖然get shell拿到了flag,但根據flag形式需要用ret2dl_solve,等我用ret2dl_solve解決時再來補wp

reverse

0x00 helloworld:

0x01 simple:

都是水題,不再細說

0x03 pyyy

這一題用uncomplye6或者線上網站反編譯後,得到的python程式碼都有大量的lambda操作,讀起來十分費力,並且程式碼不能直接執行,仔細觀察,程式碼中有一行

​ this_is = 'Y-Combinator'

似乎是在暗示什麼,google了一發,在 https://rosettacode.org/wiki/Y_combinator#Python 找到了Y-Combinator的介紹,即利用lambda等操作實現遞迴,於是按照網站上給出的python例項修改了程式碼,使其能夠正確執行。再仔細觀察,發現只要我們每輪的輸入都和程式每輪計算的值相等即可得到flag,因此直接修改python程式碼,讓輸入等於程式計算好的值,修改好的程式碼如下:

 1 # uncompyle6 version 2.12.0
 2 # Python bytecode 2.7 (62211)
 3 # Decompiled from: Python 2.7.13 (default, Jan 19 2017, 14:48:08) 
 4 # [GCC 6.3.0 20170118]
 5 # Embedded file name: pyyy.py
 6 # Compiled at: 2016-06-12 01:14:31
 7 from fractions import gcd
 8 __import__('sys').setrecursionlimit(1048576)
 9 data = 'Tt1PJbKTTP+nCqHvVwojv9K8AmPWx1q1UCC7yAxMRIpddAlH+oIHgTET7KHS1SIZshfo2DOu8dUt6wORBvNVBpUSsuHa0S78KG+SCQtB2lr4c1RPbMf0nR9SeSm1ptEY37y310SJMY28u6m4Y44qniGTi39ToHRTyxwsbHVuEjf480eeYAfSVvpWvS8Oy2bjvy0QMVEMSkyJ9p1QlGgyg3mUnNCpSb96VgCaUe4aFu4YbOnOV3HUgYcgXs7IcCELyUeUci7mN8HSvNc93sST6mKl5SDryngxuURkmqLB3azioL6MLWZTg69j6dflQIhr8RvOLNwRURYRKa1g7CKkmhN4RytXn4nyK2UM/SoR+ntja1scBJTUo0I31x1wBJpT4HjDN47FLQWIkRW+2wnB3eEwO5+uSiQpzA8VaH7VGRrlU/BFW4GqbaepzKPLdXQFBkNyBKzqzR/zA2GIrYbLIVScWJ19DqJCOyVLGeVIVXyzN1y327orYL2Ee3lRITnE3FouicRStaznIcw8xmxvukwVMRZIJ/vTu8Zc1WQIYEIFXMHozGuvzZgROZTyFihWNRCBBtoP9DJJALJb0pA1IKIb2zLh+pwGF40Y6y93D6weKejGPO+A0DBXH9vuLcCcCIvr/XPQhO3jLKCBN+h9unuJKW3dyWxyaVPdR2V+BTw10VXolo7yaTH1GbR4TiVSB308mBOMwfchwihEe7RdMXvmXgaGarKkJe0NLUCd8jwhYII+WymjxO/xOz/ppOvNfAyIQksW0sggRPQTlgXSZ7MIVA1h66sGNljJ833MoFzWof3azLabaz1OrAJFqYXBg/myDsy1tV6rULSQ82hVR/TNnSmBGvyEDJTrLSwHyj78NOrW4mUnlLGBnAgWfw6pW2lRK2jkNX9NM6DfLsRK8lwl85UP8CZSuNdcLmLwHTVMZGm/cNkZCtWRBlZqEggxGdIO44D+f4y6ysnAk5/QzEwjIuecxEOb0jyV6dFui8g0c3Oxlhzcli0X8ToJFyeQRv1N9nokYZ07tFlG6m18kCToKz1qiH1U7kljXa6SvdORur5dWYLQ//gwhwppe7JlNda/cEoh92h96wRZDv1dSK/f1vz+mUeUyUlFY0iMjfw5eBXWZppNZi3ZtJcq5kllM2ACVFcxQWI3azM3ArOcqjosoiPjNoDYgKh7w4k2Cd0kLYEHscz/njtJ1KEcwLtqs4nJ+gB2r4V9g03YgvY5E8JJtfJMKdaTedjtvEuif8FNlCK9DMnL1iLpWptJbdfO83Y7Y46XCqjZFBI5o9Qtb78nLhMEM5/YTaNOM/wE/oJl5HI/i1X6kW3PKCsVubRkOkc2xawl6NYdLETjLvmrGhhI'
10 a = 138429774382724799266162638867586769792748493609302140496533867008095173455879947894779596310639574974753192434052788523153034589364467968354251594963074151184337695885797721664543377136576728391441971163150867881230659356864392306243566560400813331657921013491282868612767612765572674016169587707802180184907L
11 b = 166973306488837616386657525560867472072892600582336170876582087259745204609621953127155704341986656998388476384268944991674622137321564169015892277394676111821625785660520124854949115848029992901570017003426516060587542151508457828993393269285811192061921777841414081024007246548176106270807755753959299347499L
12 c = 139406975904616010993781070968929386959137770161716276206009304788138064464003872600873092175794194742278065731836036319691820923110824297438873852431436552084682500678960815829913952504299121961851611486307770895268480972697776808108762998982519628673363727353417882436601914441385329576073198101416778820619L
13 d = 120247815040203971878156401336064195859617475109255488973983177090503841094270099798091750950310387020985631462241773194856928204176366565203099326711551950860726971729471331094591029476222036323301387584932169743858328653144427714133805588252752063520123349229781762269259290641902996030408389845608487018053L
14 e = 104267926052681232399022097693567945566792104266393042997592419084595590842792587289837162127972340402399483206179123720857893336658554734721858861632513815134558092263747423069663471743032485002524258053046479965386191422139115548526476836214275044776929064607168983831792995196973781849976905066967868513707L
15 F = (a, b, c, d, e)
16 m = 8804961678093749244362737710317041066205860704668932527558424153061050650933657852195829452594083176433024286784373401822915616916582813941258471733233011L
17 g = 67051725181167609293818569777421162357707866659797065037224862389521658445401L
18 z = []
19 for i, f in enumerate(F):
20     n = pow(f, m, g)
21     #https://rosettacode.org/wiki/Y_combinator#Python
22     this_is = 'Y-Combinator'
23     #  l = (lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))))
24     Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
25     fun = lambda f: lambda x: (1 if x < 2 else f(x - 1) * x % n)
26     l = Y(fun)(g % 27777)
27 #   Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
28 #   fac = lambda f: lambda n: (1 if n<2 else n*f(n-1))
29 #  >>> [ Y(fac)(i) for i in range(10) ]
30 #  [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
31 #  >>> fib = lambda f: lambda n: 0 if n == 0 else (1 if n == 1 else f(n-1) + f(n-2))
32 #  >>> [ Y(fib)(i) for i in range(10) ]
33 #  [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
34     #  print l
35     #  c = raw_input('Channenge #%d:' % i)
36     c = l
37     if int(c) != l:
38         print 'Wrong~'
39         exit()
40     z.append(l)
41 
42 z.sort()
43 gg = '(flaSg\'7 \\h#GiQwt~66\x0csxCN]4sT{? Zx YCf6S>|~`\x0c$/}\'\r:4DjJFvm]([sP%FMY"@=YS;CQ7T#zx42#$S_j0\\Lu^N31=r\x0b\t\tjVhhb_KM$|6]\nl!:V\rx8P[0m ;ho_\rR(0/~9HgE8!ec*AsGd[e|2&h!}GLGt\'=$\x0cbKFMnbez-q\\`I~];@$y#bj9K0xmI2#8 sl^[email protected]\x0b\\9Ohf]c>Vj/>rnWXgLP#<[email protected],\'n a_7C:-}f(WO8Y\x0c2|(nTP!\'\\>^\'}-7+AwBV!w7KUq4Qpg\tf.}Z7_!m+ypy=`3#\\=?9B4=?^}&\'~ [email protected]\n0=6\x0b\tv\nl!G\'y4dQW5!~g~I*f"rz1{qQH{G9\x0c\'b\x0cp\x0bdu!2/\\@i4eG"If0A{-)N=6GMC<U5/ds\rG&z>P1\nsq=5>dFZUWtjv\tX~^?9?Irwx\\5A!32N\x0bcVkx!f)sVY Men\x0c\'ujN<"LJ\x0c5R4"\\\\XPVA\'m$~tj)Br}C}&kX2<|\np3XtaHB.P\'(E 4$dm!uDyC%u ["x[VYw=1aDJ (8V/a!J?`_r:n7J88!a25AZ]#,ab?{%e\x0b]wN_}*Q:mh>@]u\t&6:Z*Fmr?U`[email protected]&5~L ,\tQ18 -Hg q2nz%\x0ccUm=dz&h1(ozoZ)mrA=`HKo\n\'rXm}Z-l3]WgN\\NW<{o=)[V({7<N1.-A8S"=;3sderb\tOZ$K\r0o/5\x0bMc76EGCWJ3IQpr7!QhbgzX8uGe3<w-g\'/j\'\tM4|9l?i&tm_\[email protected]@%L_\r)&/q=LZa(%}""#if#Kq74xK?`jGFOn"8&^3Q-\r#]E$=!b^In0:$4VKPXP0UK=IK)Y\rstOT40=?DyHor8j7O\\r/~ncJ5];cCT)c?OS0EM5m#V(-%"Tu:!UsE],0Dp  [email protected]]J{%oH54B&(zE.(@5#2k\tJnNlnUEij\\.q/3HBpJNk*X(k5;DlqK\'\'fX\r}EBk_7\x0b:>8~\[email protected]({/U}1}#TqjreG\nN{\rX>4EsJr0Pn\\Z\\aL/-U<<{,Q;j\tF=7f\')+wH:p{G=_.s\\t-\x0bI\x0c*y\t1P:Y|/2xE<uo]~$>5k]FW+>fR<QA"(Fj[LL(hzfQo#PJ;:*0kB~3]9uL[o.xue:VQ\t;9-Tu\tq|mzzhV_okP\t,d\rQ`]5Gf\x0c#gXB\x0cAH|)NI|K=KW-&p-<b"3e.rO\x0cuK=\x0c^\r+MuLxCJ`UKaD\x0bBH&n+YVajZ(U7pwWtto3T10VLHwSJ\rK\t}\'F$l1:b2Bd\na=#t0iq}#!{1_)w$}<Dp(borC\'\t?r6;,+k;a([email protected]?RCWYEDrjZe![x=n_%S]rl{&fLr*mgCD;92/nNsaxKy/;\nr]sPK=`+YP>MmfB\n8O4/"}nE7r*=41f2\t37>K\'s$wpl;qS[`qzu\x0b\t\nuaU|b,C`4& dRN~]7DnuTb2FhNHV!#Z2Hho\x0b[%.{O\t$q0\[email protected][email protected][I^{JL|O8]i8{p)A.w)14qK3JoyF%licZ~ga\rW[L:W\rtIvfWJjZUOvB\[email protected]|PexJ Pcw1\ry6!63B}]J])6fak/3r]W\tMeXt[uc(1_U lys{a1X\r%)[wwP3rhgNW{*d~_E%[email protected]^0=\x0bwT\ni4/V;_\nM1rb?w~Q)Dli4u\n`}1+D8"\t`@V~$9l$Uy**VnI (@Ga0<RxfmoNgJTtE-aLH\rE5fMy7rk$)V\rL2Fv/AivOa"\nuX|70Xrw^D]%i%JyT\x0cc%cwZ/Wbp=IiY;/@nFEe>3=tM;K*`fReGoc5V/Ri?nXZ-RW)\'\t<\x0cV>@[email protected]%sO%},B_pjc`s"@[email protected]?mb\'?Q:F\x0bLJkPgjaFAc=rbrjAz$Zz\x0cq0GU!")xFOEF(x!3M\t:l83|}}HgGJJ#eT/I\x0b[|lK_n+;Wi/N^B4LzL.a(gVWq,zO6\'S|tb>RX` ca*CO<w\x0ci =wc1,M~\x0bc`FYEs\r){+Ll8[I9-88m\t\\iK/\\hno-C[vX*3Hx:%:K\rt\x0cW!tj\'SOhqxP|k7cw [email protected]?P\'HmapG7$0#T(Auz]sjmd#\rFP/}[email protected](d%dZKLZ2LK\'e_E\x0bQmR 5/(irq4-EUyp<hB?[\tnU:p*xuzASM'
44 
45 Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
46 fun = lambda f: lambda n: (1 if n < 3 else f(n - 1) + f(n - 2))
47 
48 print ''.join((gg[Y(fun)(i + 2)] for i in range(16))) % ''.join((data[pow((gcd(z[i % 5], z[(i + 1) % 5]) * 2 + 1) * g, F[i % 5] * (i * 2 + 1), len(data))] for i in range(32)))
49 #   Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
50 #   fac = lambda f: lambda n: (1 if n<2 else n*f(n-1))
51 #  >>> [ Y(fac)(i) for i in range(10) ]
52 #  [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
53 #  >>> fib = lambda f: lambda n: 0 if n == 0 else (1 if n == 1 else f(n-1) + f(n-2))
54 #  >>> [ Y(fib)(i) for i in range(10) ]
55 #  [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
56 #  okay decompiling pyyy.pyc

執行即可

0x04 accumulator

這個題目中check函式的F5程式碼有點混亂,單看虛擬碼很難分析,但通過除錯可以快速確定check函式的整體流程是對輸入的每一位求和,然後與0x601080這個陣列進行比較,在這個過程中更新0x6013C0和0x6013B0兩個全域性變數用於定址,程式的整體流程是先check(sha512(input)),然後check(input),因為sha512是雜湊函式,因此放棄了逆向求解。仔細分析,在check(input)時,使用的也是對input每一位求和,然後與陣列比較的方法,而與第一次check(sha512(input))相比僅僅是兩個用於定址的全域性變數不同,這樣我們只要對陣列逐位做差即可

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 __Auther__ = 'M4x'
 4 
 5 data = [195, 255, 493, 584, 799, 929, 946, 1086, 1180, 1184, 1421, 1595, 1805, 1846, 2081, 2320, 2430, 2605, 2727, 2972, 3213, 3403, 3418, 3649, 3712, 3950, 3989, 4193, 4228, 4394, 4523, 4624, 4706, 4935, 4999, 5072, 5106, 5291, 5510, 5536, 5644, 5751, 5993, 6118, 6126, 6198, 6211, 6410, 6469, 6609, 6647, 6752, 6978, 7010, 7053, 7106, 7274, 7468, 7563, 7673, 7706, 7956, 8146, 8187, 8257, 8333, 8398, 8469, 8592, 8640, 8693, 8742, 8793, 8844, 8901, 8953, 9007, 9062, 9113, 9161, 9215, 9317, 9374, 9429, 9483, 9540, 9591, 9644, 9692, 9741, 9792, 9846, 9944, 9996, 10045, 10144, 10195, 10246, 10294, 10350, 10402, 10450, 10551, 10652, 10750, 10849, 10946, 11045, 11096, 11147, 11202, 11304, 11353, 11451, 11507, 11605, 11653, 11753, 11852, 11900, 11951, 12052, 12105, 12161, 12259, 12360, 12409, 12461, 12563, 12664, 12718, 12775, 12823, 12921, 12970, 13020, 13071, 13173, 13227, 13276, 13374, 13422, 13521, 13569, 13667, 13718, 13771, 13873, 13972, 14029, 14080, 14179, 14278, 14377, 14432, 14482, 14531, 14579, 14627, 14679, 14732, 14789, 14840, 14894, 14951, 15052, 15154, 15210, 15263, 15314, 15363, 15460, 15509, 15610, 15666, 15763, 15818, 15916, 15968, 16018, 16075, 16132, 16233, 16288, 16386, 16443, 16543, 16600, 16655, 16703, 16801, 16858, 16955, 17005, 17056, 17153, 17250, 17375]
 6 
 7 flag = ""
 8 for i in xrange(1, len(data)):
 9     flag += chr(data[i] - data[i - 1])
10 
11 print flag
View Code

再介紹一個IDA使用的trick,在提取陣列時,先在陣列的第一位上點d,使陣列的型別轉換成dd,然後再點a,使IDA把這一段連續的資料轉成陣列,然後可以使用alt+L快速選中陣列範圍(alt+L,滑鼠滾輪,afl+L),然後通過shift+E就可以快速匯出我們想要選擇的資料了

0x05 gccc

該題的邏輯很簡單,只有一個輸入,下載檔案後發現gccc.exe為.Net(C#)架構,因此用C#的反編譯工具(這裡用了)反編譯得到題目程式碼如下:

// GrayCCC
public static void Main()
{
    Console.Write("Input the key: ");
    uint num;
    if (!uint.TryParse(Console.ReadLine().Trim(), out num))
    {
        Console.WriteLine("Invalid key");
        return;
    }
    string text = "";
    string text2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ{} ";
    int num2 = 0;
    byte[] array = new byte[]
    {
        164, 25, 4, 130, 126, 158, 91, 199, 173, 252, 239, 143, 150, 251, 126, 
        39, 104, 104, 146, 208, 249, 9, 219, 208, 101, 182, 62, 92, 6, 27, 5, 46
    };
    byte b = 0;
    while (num != 0u)
    {
        char c = (char)(array[num2] ^ (byte)num ^ b);
        if (!text2.Contains(new string(c, 1)))
        {
            Console.WriteLine("Invalid key");
            return;
        }
        text += c;
        b ^= array[num2++];
        num >>= 1;
    }
    if (text.Substring(0, 5) != "FLAG{" || text.Substring(31, 1) != "}")
    {
        Console.WriteLine("Invalid key");
        return;
    }
    Console.WriteLine("Your flag is: " + text);
}
View Code

可以看到,邏輯很簡單,對輸入進行了32輪驗證,通過每一層驗證即可得到flag.

但邏輯簡單並不意味著容易解決,仔細分析程式碼可以看出解題的關鍵在求出num的值,num經過32次右移一位後等於0,因此取值範圍為[2 ^ 31, 2 ^ 32), 在該範圍內找到滿足32輪驗證的值即可,但在找到該值時遇到了問題,如果單純爆破的話,2 ^ 32 - 2 ^ 31次爆破遠遠超出了能接受的範圍,因此考慮用z3來解決這個多約束問題.

該題的約束條件有如下幾個:

  • 0~5位為"FLAG{"
  • 最後一位(31)為"}"
  • 6~30位需在"ABCDEFGHIJKLMNOPQRSTUVWXYZ{} "內

因此可以寫出z3的程式碼如下:

def getNum():
    b = 0
    num2 = 0
    # 2 ** 30 <= num < 2 ** 31
    s = Solver()
    num = BitVec('num', 64)
    s.add(num >= 2 ** 31)
    s.add(num < 2 ** 32)
    # s.add(num > 1510650850)

    for i in xrange(32):
        if i < 5:
            s.add(((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('FLAG{'[i]))
        elif 5 <= i < 31:
            s.add(
                    Or(    
                        And(
                            ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) >= 65, 
                            ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) <= 90, 
                        ),
                        
                        # ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('{'),
                        # ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('}'),
                        ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord(' ')

                    )
                )
                elif i == 31:
                        s.add(((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('}'))
        b ^= array[num2]
        b &= 0x7f
        num2 += 1
        num >>= 1

    if s.check() == sat:
        print s.model()
                #bug
        #  print s.model()[num].as_long()
    # while s.check() == sat:
        # print s.model()[num]
        # s.add(Or(num != s.model()[num].as_long()))
View Code

執行,num的值就秒出了

註釋掉的部分是一個還沒解決的bug,本來是想通過新增約束跑出所有的可行解,但可能是因為用了BitVec導致在資料型別的轉換上有些問題,現在只跑出了一組解.等到考完試再來修這個bug

有了num的值,接下類有很簡單了,可用python求解

#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = "M4x"

import pdb
from z3 import *
array = [164,25, 4, 130, 126, 158, 91, 199, 173, 252, 239, 143, 150, 
251, 126, 39, 104, 104, 146, 208, 249, 9, 219, 208, 101, 
182, 62, 92, 6, 27, 5, 46]
# print len(array)

def getNum():
    b = 0
    num2 = 0
    # 2 ** 30 <= num < 2 ** 31
    s = Solver()
    num = BitVec('num', 64)
    s.add(num >= 2 ** 31)
    s.add(num < 2 ** 32)
    # s.add(num > 1510650850)

    for i in xrange(32):
        if i < 5:
            s.add(((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('FLAG{'[i]))
        elif 5 <= i < 31:
            s.add(
                    Or(    
                        And(
                            ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) >= 65, 
                            ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) <= 90, 
                        ),
                        
                        # ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('{'),
                        # ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('}'),
                        ((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord(' ')

                    )
                )
                elif i == 31:
                        s.add(((array[num2] ^ (num & 0x7f) ^ b) & 0x7f) == ord('}'))
        b ^= array[num2]
        b &= 0x7f
        num2 += 1
        num >>= 1

    if s.check() == sat:
        print s.model()
                #bug
        #  print s.model()[num].as_long()
    # while s.check() == sat:
        # print s.model()[num]
        # s.add(Or(num != s.model()[num].as_long()))

def
            
           

相關推薦

hackme.inndy.tw一些Writeup530更新

hackme.inndy.tw的一些Writeup(6月3日更新) 推薦一下https://hackme.inndy.tw/scoreboard/,上邊有一些很好的針對新手的題目,但網上能搜到的Writeup很少,因此開了這篇博文記錄一下部分目前解出的題目(主要是pwn和re),以後會跟著解題進度的推進

Linux20180502 六周第四次課52日

sed 練習 六周第四次課(5月2日)復習 擴展打印某行到某行之間的內容http://ask.apelearn.com/question/559 sed轉換大小寫 http://ask.apelearn.com/question/7758 sed在某一行最後添加一個數字http://ask.apelearn.

Linux20180503 六周第五次課53日awk擴展

awk 六周第五次課(5月3日)復習擴展awk 中使用外部shell變量http://ask.apelearn.com/question/199 相對來說,awk更像是一種簡單的編程語言 A=44echo "ABCD" | awk -v GET_A=$A ’{print GET_A}’說明

LiNUX20180509七周四次課59日IPTABLES

iptables nat七周四次課(5月9日)10.15 iptables filter表案例10.16/10.17/10.18 iptables nat表應用擴展1. iptables應用在一個網段 http://www.aminglinux.com/bbs/thread-177-1-1.html2. sa

Linux學習筆記十四周一次課59日

NFS介紹 NFS服務端安裝 NFS客戶端安裝 14.1 NFS介紹14.2 NFS服務端安裝配置服務端安裝nfs-utils和rpcbind;客戶端只安裝nfs-utils#yum install -y nfs-utils rpcbind#vim /etc/exports /home/nfste

Linux學習筆記十四周二次課510日

exportfs NFS客戶端問題 FTP vsftpd搭建ftp 14.4 exportfs命令#exportfs -arv //重新掛載#vim /etc/exports/tmp/192.168.133.0/24(rw,sync,no_root_squash)14.5 NFS客戶端問題#m

linux十四周四次課514日

linux 筆記 十四周四次課(5月14日)16.1 Tomcat介紹16.2 安裝jdk16.3 安裝Tomcat 16.1 Tomcat介紹 16.2 安裝jdk 打開下載網址。 點擊jdk=8u144-linux-x64.tar.gz ,下載 傳到linux ctrl+F打開xftp 把下載的文

832. Flipping an Image 522日

div cpp ID solution 方法 and for return amp 解答 class Solution { public: vector<vector<int>> flipAndInvertImage(vector<ve

811. Subdomain Visit Count 523日

sys 有符號 using pan bstr leet double類型 sim clas 解答 class Solution { public: vector<string> subdomainVisits(vector<string>&a

500. Keyboard Row 526日

() tor push_back str DC Go row ons OS 解答 class Solution { public: vector<string> findWords(vector<string>& words) {

840. Magic Squares In Grid 527日

() || 二次 邏輯 優先 pla sub 分析 flag 開頭 這是每周比賽中的第一道題,博主試了好幾次坑後才勉強做對了,第二道題寫的差不多結果去試時結果比賽已經已經結束了(尷尬),所以今天只記錄第一道題吧 題目原文 Magic Squares In Grid A

463. Island Perimeter 529日

amp lan clas num size sla 只需要 ++ PE 解答 class Solution { public: int islandPerimeter(vector<vector<int>>& grid) {

AWSome Day廣州511日等你來! – 運維派

2017上半年 AWSome Day收官站 廣州開啟! AWS傾情出品 Amazon Web Services (AWS) 誠摯邀請您,參加2017年AWSome Day雲端計算免費培訓。 在2017上半年,本活動將在廣州收官 5月11日,AWSome Day移師羊城 廣州站期待你的到來! 點選

二周第二次課130

應該 刪除 發現 .com ks.cfg roo 環境變量 conda 圖片 2.10 環境變量PATH2.11 cp命令2.12 mv命令2.13 文檔查看cat/more/less/head/tail2.10 環境變量PATH!!!回顧#which 可以查看命令的所處路

八周二次課130

目標 ima sha 常用 機器 mar rsync命令 通過 端口 八周二次課(1月30日)10.28 rsync工具介紹rsync -av /etc/passwd /tmp/1.txt 把passwdcp到tmp下並命名為1.txtrsync -av /etc/pass

八周二次課130 10.28 rsync工具介紹 10.29/10.30 rsync常用選項 10.31 rsync通過ssh同步

limit 遠程服務 cas comment foo sid har systemd nsf 八周二次課(1月30日)10.28 rsync工具介紹10.29/10.30 rsync常用選項10.31 rsync通過ssh同步========================

Linux學習筆記第二周第二次課130

二次 行號 文檔查看 筆記 默認 ng2 覆蓋 cp命令 文件 2.10 環境變量PATH查命令路徑及別名#which rm顯示當前環境變量#echo $PATH命令也是一個文件,可以用絕對路徑當命令使用;命令想直接使用,必須加入到環境變量PATH裏#PATH=$PATH:

Linux學習筆記第八周五次課330

df du fdisk groupadd mke2fs 復習(今日無答疑,筆記可以不寫)三、用戶和組管理3.1 用戶配置文件和密碼配置文件5/etc/passwd用戶賬號密碼文件,配置文件;第一列,用戶名;第二列,密碼全用X表示;第三列,UID;第四列,GID;第五列,註釋;第六列,用戶家

?十四周五次課330

linux十四周五次課(3月30日)16.4 配置Tomcat監聽80端口Tomcat默認是監聽8080端口,本節學習如何配置讓它監聽80端口.編輯Tomcat配置文件:vim /usr/local/tomcat/conf/server.xml將Connector port="8080"

Linux 20180413 二周第五次課330 2.23 find命令 文件名後綴

find 命令Find 是用來搜索文件的命令which 接上命令是用來查找這個命令在環境變量中的位置whereis 也可以找, 但是需要update 數據庫才可以locate命令是需要安裝的,安裝命令 yum install -y mlocate 記得要updatedb介紹幾個常用的快捷鍵ctrl c 終止當