X-NUCA2018线上赛 Writeup

2018-12-24 14:39:44 浏览数 (1)

前言

据说变成了PWN专场,不过在CRYPTO大佬和RE大佬的发挥下,最后拿到密码三血排名12,拿到决赛的入场券,希望我队的pwn师傅能早日成长起来。

web

ezdotso

源码中说明 第一,不能有0-9a-zA-Z /*之外的字符 第二,这个字符串以a-zA-Z开头,中间有空格,0-9a-zA-Z/*结尾

直接构造

http://7dcccb1f119149a2ba345d9c2ef3144b4d955441e0084ff7.game.ichunqiu.com/?action=cmd&cmd=ls /就能读目录

存在flag

构造payload

http://7dcccb1f119149a2ba345d9c2ef3144b4d955441e0084ff7.game.ichunqiu.com/?action=cmd&cmd=cat /flag

读到flag

flag{b99fdc7e-b57c-4f3b-9cd1-cd63ce438920}

reverse

Code_Interpreter

简单的vm题,简述如下: 程序通过读取 code文件进行相应的解析,总共用到了4个寄存器(可以这么认为),最后根据 bytecode写出加密过程,解三元一次方程组,列好约束条件,利用 z3可解比较简单。

bytecode如下:

代码语言:javascript复制
op in1 in2
09 04 04        data[in1]^=data[in2]                    r4 =0
09 00 00        data[in1]^=data[in2]                    r0 = 0
08 01 00        data[in1]=data2[in2  dword_6024BC]       r1=d0  first
08 02 01        data[in1]=data2[in2  dword_6024BC]       r2=d1  second
08 03 02        data[in1]=data2[in2  dword_6024BC]       r3=d2  thrid 
06 01 04        data[in1]=data[in1]>>in2                 r1=r1>>4  high byte
05 01 15        data[in1]*=in2                          r1*=0x15
07 00 01        data[in1]=data[in2]                     r0=r1
04 00 03        data[in1]-=data[in2]                    r0-=r3
01 6B CC 7E 1D  dword_6020A0[  dword_6024B8]=1d7ecc6b   dword_6024B8 = 3    d3=1d7ecc6b
08 01 03        data[in1]=data2[in2  dword_6024BC]      r1=d3=1d7ecc6b
04 00 01        data[in1]-=data[in2]                    r0-=r1
02              --dword_6024B8                          dword_6024B8=2
0A 04 00        data[in1]|=data[in2]                    r4|=r0

09 00 00        data[in1]^=data[in2]                    r0=0
08 01 00        data[in1]=data2[in2  dword_6024BC]      r1=d0
08 02 01        data[in1]=data2[in2  dword_6024BC]      r2=d1
08 03 02        data[in1]=data2[in2  dword_6024BC]      r3=d2
06 03 08        data[in1]=data[in1]>>in2                r3=r3>>8
05 03 03        data[in1]*=in2                          r3*=3
07 00 03        data[in1]=data[in2]                     r0=r3
03 00 02        data[in1] =data[in2]                    r0 =r2
01 7C 79 79 60  dword_6020A0[  dword_6024B8]=6079797c   dword_6024B8 = 3    d3=6079797c
08 01 03        data[in1]=data2[in2  dword_6024BC]      r1=d3=6079797c
04 00 01        data[in1]-=data[in2]                    r0-=r1
02              --dword_6024B0                          dword_6024B8 = 2
0A 04 00        data[in1]|=data[in2]                    r4|=r0

09 00 00        data[in1]^=data[in2]                    r0=0
08 01 00        data[in1]=data2[in2  dword_6024BC]      r1=d0
08 02 01        data[in1]=data2[in2  dword_6024BC]      r2=d1
08 03 02        data[in1]=data2[in2  dword_6024BC]      r3=d2
06 01 08        data[in1]=data[in1]>>in2                r1=r1>>8
07 00 01        data[in1]=data[in2]                     r0=r1
03 00 02        data[in1] =data[in2]                    r0 =r2
01 BD BD BC 5F  dword_6020A0[  dword_6024B8]=5fbcbdbd   d3=5fbcbdbd
08 01 03        data[in1]=data2[in2  dword_6024BC]      r1=d3=5fbcbdbd
04 00 01        data[in1]-=data[in2]                    r0-=r1
02              --dword_6024B8                          =2
0A 04 00        data[in1]|=data[in2]                    r4|=r0

00              exit                
 result:

 check:
 dword_6024B0==0
 first=94
 second=94
 third=94

exp如下:

代码语言:javascript复制
# (d0>>4)*0x15-d2-0x1d7ecc6b =0
# (d2>>8)*3 d1-0x6079797c =0
# (d0>>8) d1-0x5fbcbdbd   =0

from z3 import *
s = Solver()
a = [BitVec("a%d"%i,32) for i in range(3)]
s.add(((((a[0]>>4))*0x15-a[2]-0x1d7ecc6b)==0))
s.add((((a[2]>>8))*3 a[1]-0x6079797c)==0)
s.add((((a[0]>>8)) a[1]-0x5fbcbdbd)==0)
s.add((a[0]&0xff)==0x5e)
# s.add((a[1]&0xff)==0x5e)
s.add((a[1]&0x0ff0000)==0x5e0000)
print s.check()
m=s.model()
magic=[]
for i in a:
    magic.append((m[i].as_long()))
print magic
#['0x5e5f5e70', '0x5f5e5e5f', '0x5e5e5f88']
#['0x5e5f5e5e', '0x5f5e5e5f', '0x5e5e5f5e']

Strange Interpreter

控制流平坦化,之前有个去混淆的脚本,由于没有环境,也就没弄。 先是尝试了下pintools,不过没跑出来,手动尝试了好久,只推出了前面两个字符,可能是脚本的问题。 我的pintools的脚本如下:

代码语言:javascript复制
import subprocess
import os
import logging
import json
import string
import time
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class shell(object):
    def runCmd(self, cmd):
        res = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        sout, serr = res.communicate()
        return res.returncode, sout, serr, res.pid

    def initPin(self, cmd):
        res = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        self.res = res

    def pinWrite(self, input):
        self.res.stdin.write(input)

    def pinRun(self):
        sout, serr = self.res.communicate()
        return sout, serr


filename = "./strange"
cmd = "/home/jeb/Desktop/pin-3.7-97619-g0d0c92f4f-gcc-linux/pin -t "   
    "/home/jeb/Desktop/pin-3.7-97619-g0d0c92f4f-gcc-linux/source/tools/ManualExamples/obj-intel64/inscount0.so"   " -- "   filename
shell = shell()
dic = string.letters '_ */' string.digits
cout_old = 624222
cur='X-NUCA{'
for i in range(32):
    for s in dic:
        shell.initPin(cmd)
        pwd = cur s '?'*(31-len(cur))
        print pwd
        print len(pwd)
        shell.pinWrite(pwd 'n')
        sout,serr = shell.pinRun()
        print sout
        cout = sout.split("Count ")[1]
        print cout
        cout_sub=  cout_old - int(cout)
        cout_old = int(cout)
        if cout_sub > 35 and cout_sub < 100:
            cur=cur s
            break
        print ("current cur ", cur,"current count:",cout,"sub_count ",cout_sub)

然后带着混淆,调试了一遍,发现加密过程很简单,无非是几个xor,dump出数据便可解决。

首先搜索字符串,交叉引用到 loc_412385 中,可以看出这里便是最后的校验部分,程序使用llvm混淆,中间的过程全是无关的指令,着重关注最后那10多个基本块即可!在每个基本块的位置下断点,然后不断 F9 很容易看出程序的大致流程。

代码语言:javascript复制
1.先是逐字节拷贝输入
2.异或数据的初始化
3.一些无关紧要的乘法运算
4.对前0x10字节进行xor
5.再次生成xor数据
6.对后0x10字节进行xor
7.最后同12345abcdefghijklmnopqrstuvwxyz进行比较,可以看出都是xor运算

最后一个xor如图

exp如下:

代码语言:javascript复制
dic = '012345abcdefghijklmnopqrstuvwxyz'
dic_list=list(dic)
xor1=[0x68,0x1C,0x7C,0x66,0x77,0x74,0x1A,0x57,0x06,0x53,0x52,0x53,0x02,0x5D,0x0C,0x5D]


xor2=[0x04,0x74,0x46,0x0E,0x49,0x06,0x3D,0x72,0x73,0x76,0x27,0x74,0x25,0x78,0x79,0x30]

xor3=[0x68,0x1C,0x7C,0x66,0x77,0x74,0x1A,0x57,0x06,0x53,0x52,0x53,0x02,0x5D,0x0C,0x5D]

print len(xor1)
flag1=""
for i in range(16):
    flag1 =chr(xor1[i]^ord(dic[i]))
print flag1
flag2=""
for i in range(16):
    j=i 16
    flag2 =chr(xor2[i]^ord(dic[j])^xor3[i]^ord(dic[i]))
print flag2
print flag1 flag2

crypto

Warm Up

源码分析后发现是rsa加密的套路 原本认为是在e处存在攻击点 收来仔细查看数据包发现数据流0和3中的N值相同 可以进行共模攻击 exp如下:

代码语言:javascript复制
import gmpy2 as gm
    from Crypto.Util.number import long_to_bytes
    N1 = 25118186052801903419891574512806521370646053661385577314262283167479853375867074736882903917202574957661470179148882538361560784362740207649620536746860883395110443930778132343642295247749797041449601967434690280754279589691669366595486824752597992245067619256368446164574344449914827664991591873150416287647528776014468498025993455819767004213726389160036077170973994848480739499052481386539293425983093644799960322581437734560001018025823047877932105216362961838959964371333287407071080250979421489210165485908404019927393053325809061787560294489911475978342741920115134298253806238766543518220987363050115050813263
    N2 = 16469436076891819107430664586570790058365332532674438789146675997314595491187244459383921424835032067061885275554735557145712521498253296163910390306330135855302922157272936907898045006260883274333834229418152155694295570782207999565052765330228242362968933298758811404031322069181362855243705838799645685066332172969401743211750904509226291946662578751991715996103303976647730874845283020815000321892678220724802450248872234664036667264022384588371373249390642053539194423282694248940736528696713895935252137917260856321114370743803866601761211552228903425850365457360876898940583221394582723557605309072232855822121
    N3 = 25118874053328546753024263989563415727502048075025991833569501205632242337113077901532332374775395419348348701048189408092632079814832363732010926177912082562964016670890936281050864496155721672281093344082281963638371977758361202131970609490512245265719538879695944721744492357697438865016952531556200322390888505552979421131419142724258271230059422420336363879787201072494558351266967920357858873458121748582985640375604986741727501058494951533532341125506734541216305271046143705754799910729045435564538502962145048652820879590895993225869189429946329168385872964357133780290864454638364009252548494323438022231349
    N4 = 25118186052801903419891574512806521370646053661385577314262283167479853375867074736882903917202574957661470179148882538361560784362740207649620536746860883395110443930778132343642295247749797041449601967434690280754279589691669366595486824752597992245067619256368446164574344449914827664991591873150416287647528776014468498025993455819767004213726389160036077170973994848480739499052481386539293425983093644799960322581437734560001018025823047877932105216362961838959964371333287407071080250979421489210165485908404019927393053325809061787560294489911475978342741920115134298253806238766543518220987363050115050813263
    N5 = 22890921296489391468723563207482439368715048528954857727696611997213849453925407639478311064849002092841332187029922829503732594819405334557899018193836573827538367732876315261107786375883032702336369949813383359822945447348738639898488349249930794685147680602369574583272233186638639006722932514492412473499671240672786609392623108668740611409192410353088792926863759136574234682712437658167544420388503462191966664297486016864300587100339017032869018550693788156823952834586915180769842001379726271815407042736414817319930070363123671954772200618698975099285175523273493454655068815092164026790575552599814897599019
    N6 = 13610734669757105262564498565903016628884897465642188626977712600469428943454859353288561953332071112838192895353839306728698072861317475483364599428738408203420859463545743033507453999902768670963760117002226738834212826866972790759618857592183639430006129961804969344458099739275801744555852908477399106370903274847008168191406212026496201683437988789750311357127030874197256108087969060429116893649257007863251857384220793898187863784143099430027004383026281731367512474585221423627626454894508617409600974924819458907176960087389776551021286749078138520414178131682409288175569603840517742966654020297053280120421
    Ns = [N1, N2, N3, N4, N5, N6]

    for i in range(6):
        for j in range(i 1, 6):
            print i, j, gm.gcd(Ns[i], Ns[j])
    #通过这个循环发现了有两个N是一样的

    e1 = 7669
    e2 = 6947
    c1 = 22917655888781915689291442748409371798632133107968171254672911561608350738343707972881819762532175014157796940212073777351362314385074785400758102594348355578275080626269137543136225022579321107199602856290254696227966436244618441350564667872879196269074433751811632437228139470723203848006803856868237706401868436321225656126491701750534688966280578771996021459620472731406728379628286405214996461164892486734170662556518782043881759918394674517409304629842710180023814702447187081112856416034885511215626693534876901484105593275741829434329109239483368867518384522955176807332437540578688867077569728548513876841471
    c2 = 20494665879116666159961016125949070097530413770391893858215547229071116025581822729798313796823204861624912909030975450742122802775879194445232064367771036011021366123393917354134849911675307877324103834871288513274457941036453477034798647182106422619504345055259543675752998330786906376830335403339610903547255965127196315113331300512641046933227008101401416026809256813221480604662012101542846479052832128788279031727880750642499329041780372405567816904384164559191879422615238580181357183882111249939492668328771614509476229785062819586796660370798030562805224704497570446844131650030075004901216141893420140140568

    def sameModula(n, e1, e2, c1, c2):
        g, x1, x2 = gm.gcdext(e1, e2)
        if x1 < 0:
            c1 = gm.invert(c1, n)
            x1 = -x1
        if x2 < 0:
            c2 = gm.invert(c2, n)
            x2 = -x2
        p = gm.powmod(c1, x1, n) * gm.powmod(c2, x2, n) % n
        return p

    m = sameModula(N1, e1, e2, c1, c2)
    print m
    print long_to_bytes(m)

得到flag FLAG{g00dLuck&Hav3Fun}

baby_crypto

题目

代码语言:javascript复制
The 26 letters a, b, c, ..., y, z correspond to the integers 0, 1, 2, ..., 25
len(key_a) = m
len(key_k) = n
c[i] = (p[i] * key_a[i % m]   key_k[i % n]) % 26

p is plain text, only lowercase letters are refered to.
c is encrypted text

I have appended the flag at the end of plain text, the format of which is like 'flagis......'
Now you have the encrypted text, Good luck!

可以看出题目是将字母首先对应成了数字

而后使用长度分别为m和n的密钥keya和keyk对明文p进行了加密

关键点在于使用%m和%n是使密钥循环起来进行加密

这样不由得想起vigenere密码

代码语言:javascript复制
首先选择一个密钥

随后循环使用密钥对密文进行加密

因此破解的原理应该与破解vigenere密码原理类似

这里就可以利用重合指数的方法来对这个多表替换的密码进行分析与破解

维吉尼亚密码破解及重合指数

这是一种破解vigenere密码很好的方法

代码语言:javascript复制
在一串无规律的字母中,我们随意抽取两个字母,由于每个字母被抽到的概率相同,因此抽取的两个字母相同的概率为26*(1/26)^2=0.0385
如果是从一篇文章或者一句完整的话中抽取的概率为P(a)^2 P(b)^2 …. p(z)^2=0.0687

为了破解密钥的长度l,我们需要使用上述的重合指数

我们已经知道,维吉尼亚密码可以被分解为若干组平移密码来破译,而一个明文足够长的平移密码的重合指数接近0.0687。换言之,如果我们选取某个l值,使得分组后的密文的重合指数接近0.065,则说明选取的t值与密钥的长度是一致的。

所以这里应该先求出密钥keya和keyk长度的最小公倍数

经过爆破尝试,这里当l为6的整数倍的周期,切出来的子密文段的信息指标都很接近0.065

因此这里推测密钥keya和keyk长度的最小公倍数l为6

在求得密钥长度之后,通过穷举密钥字母的每一种可能取值(a到z总共26种),并且针对每一行求其在某一取值下重合指数,重合指数最高的情况下该行最有可能为明文原文,此时的穷举结果即为密钥字母。

由此就可以得到密钥

从而对密文进行解密

exp如下:

0 人点赞