首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>网络安全>文章内容
杂谈PHP4内核Hacking
来源:www.bugkidz.org 作者:Darkness 发布时间:2006-05-29  

此篇文章记录一下我这些天PHP HACKING的经验,乱七遭八的.各位看官莫见笑,有问题多交流.

先大概描述一下PHP内核的情况,具体点的可以参见http://cn.php.net/manual/zh/zend.overview.php.
PHP内核分为2个部分,ZEND和PHP CORE.ZEND可以说是PHP的内核的内核,他负责与操作系统沟通.分配内存,模块初始化什么什么的,他就负责了,同时把可读的脚本代码系统化.而 PHP CORE呢,负责一些外围处理,比如与SAPI沟通. APACHE,IIS啊之类的东西.php.ini他也管. 还负责网络和文件I/O.最后提醒一句,PHP是用C写的,而不是C++,大多数资源,类型.在PHP内核中需要转换.


在C语言中,套接字这样定义:SOCKET socket(int af,int type,int protocol);
在PHP中是:typedef int PHP_SOCKET;
typedef struct {
PHP_SOCKET bsd_socket;
int type ;
int error;} php_socket;
看起来是一样的.
那就来看看一个最简单的网络函数.
PHP_FUNCTION(socket_write)
{
zval *arg1;
php_socket *php_sock;
int retval, str_len;
long length;
char *str;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &arg1, &str, &str_len, &length) == FAILURE)
return;

ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket);

if (ZEND_NUM_ARGS() < 3) {
length = str_len;
}

#ifndef PHP_WIN32
retval = write(php_sock->bsd_socket, str, MIN(length, str_len));
#else
retval = send(php_sock->bsd_socket, str, min(length, str_len), 0);
#endif

if (retval < 0) {
PHP_SOCKET_ERROR(php_sock, "unable to write to socket", errno);
RETURN_FALSE;
}

RETURN_LONG(retval);
}
先说zval,ZVAL是PHP内核中的结构,里面包含任何数据类型.所以在PHP内核中最好用ZVAL,而少用char *.这样可能会带来一些安全问题.看上面的代码,好象是没问题.其实不然,看char * str.他作为第2个参数被传入.
执行socket_write ( $sock, "hellosdfs35434sdvx.plpop34][[]" );
正常得很.但是如果我们提交一个PHP数组他就会暴错了.为什么呢?虽然说char str[]从某种意义上等于char * str.但是PHP中的数组和C语言可不一样.PHP程序本身这种错误太多了,PHP的开发者可能已经习惯了.

继续PHP SOCK.zend_parse_parameters函数是得到传入参数的,当CHAR类型被传入的时候,必须指定CHAR LEN.ZEND_NUM_ARGS()是得到函数个数的.
那TSRMLS_CC是什么东西.这里得专门讲一下TSRM.他的全称是Thread Safe Resource Management.
PHP是作为网络脚本来使用的,这意味着,成千上万的人在同1秒钟里可能调用同一个页面.客户A请求的是ID1,客户B请求的是ID2,以下省去数万字.
如果没有TSRM的话,单线程的SAPI还好说,像apache,iis这样的话...那么PHP可能乱套.TSRM的作用就是保持变量的中立性.在PHP中,基本上从全局传入参数都要使用TSRM.PHP hacking中也应该使用TSRM.
ZEND_FETCH_RESOURCE(php_sock, php_socket *, &arg1, -1, le_socket_name, le_socket);
相当于是把参数1 当做PHP_SOCK类型在ZEND注册资源.
关于SOCKET_WRITE后面都是一些简单的C函数,就不谈了这个了.

关于PHP内核里面建立套接字还有一种方法,那就是用PHP流.
PHP流是很强大的,是I/O的代名词.无论是FILE I/O,还是NETWORK I/O.
拿php_stream_open_wrapper API来说,他支持FILE,TCP,UDP,SSL,SCP,HTTP,FTP.
php_stream * stream = php_stream_open_wrapper("http://www.php.net", "rb", REPORT_ERRORS, NULL);
if (stream) {
while(!php_stream_eof(stream)) {
char buf[1024];

if (php_stream_gets(stream, buf, sizeof(buf))) {
printf(buf);
} else {
break;
}
}
php_stream_close(stream);
}

看了上面的代码,可以知道的是.C语言的FILE操作函数那些 加上php_stream便可以操作php i/o了.这是官方
给的一个例子.有一点错误,PHP中printf是不能输出到网页的,要用php_printf或者zend_printf,php_write.

继续socket的话题.在php i/o中有一个API,可以把C语言的SOCKET劫持到PHP中.
php_stream * php_stream_sock_open_from_socket ( int socket, int persistent )
这样比起php_sock方便一些.

再谈谈PHP内核中的管道与命令执行.

编写API
{
建立管道
创建进程
返回输出管道
}
函数
{
传入参数处理
执行API
转换API管道为PHP流
读取PHP流
输出流
}
我看了一下SYSTEM,PASSTHRU的代码,都是由popen来完成.POPEN又由popen_ex来完成.
TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, char *env)
{
FILE *stream = NULL;
int fno, str_len = strlen(type), read, mode;
STARTUPINFO startup;
PROCESS_INFORMATION process;
SECURITY_ATTRIBUTES security;
HANDLE in, out;
char *cmd;
process_pair *proc;
TSRMLS_FETCH();

security.nLength = sizeof(SECURITY_ATTRIBUTES);
security.bInheritHandle = TRUE;
security.lpSecurityDescriptor = NULL;

if (!str_len || !CreatePipe(&in, &out, &security, 2048L)) {
return NULL;
}

memset(&startup, 0, sizeof(STARTUPINFO));
memset(&process, 0, sizeof(PROCESS_INFORMATION));

startup.cb = sizeof(STARTUPINFO);
startup.dwFlags = STARTF_USESTDHANDLES;
startup.hStdError = GetStdHandle(STD_ERROR_HANDLE);

read = (type[0] == 'r') ? TRUE : FALSE;
mode = ((str_len == 2) && (type[1] == 'b')) ? O_BINARY : O_TEXT;


if (read) {
in = dupHandle(in, FALSE);
startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startup.hStdOutput = out;
} else {
out = dupHandle(out, FALSE);
startup.hStdInput = in;
startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
}

cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c "));
sprintf(cmd, "%s /c %s", TWG(comspec), command);
if (!CreateProcess(NULL, cmd, &security, &security, security.bInheritHandle, NORMAL_PRIORITY_CLASS, env, cwd, &startup, &process)) {
return NULL;
}
free(cmd);

CloseHandle(process.hThread);
proc = process_get(NULL TSRMLS_CC);

if (read) {
fno = _open_osfhandle((long)in, _O_RDONLY | mode);
CloseHandle(out);
} else {
fno = _open_osfhandle((long)out, _O_WRONLY | mode);
CloseHandle(in);
}

stream = _fdopen(fno, type);
proc->prochnd = process.hProcess;
proc->stream = stream;
return stream;
}

process_pair是一个结构体,HANDLE和FILE.这段代码好象是一个标准的CMDSHELL.他建立了并返回了管道(文件)流.
这可是用C语言建立的管道啊,PHP并不懂的.要用PHP的一个未公开的API来完成转换.说到未公开,PHP的内核好多东西都是"未公开"的.这方面的文档少得很.

php stream * pipe = php_stream_fopen_from_file_pipe(fp,"rb");

接下来就交给php_stram系列处理了.这样就能正常输出了.


 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·一句话木马
·samcrypt.lib简介
·教你轻松查看QQ空间加密后的好友
·web sniffer 在线嗅探/online ht
·SPIKE与Peach Fuzzer相关知识
·asp,php,aspx一句话集合
·Cisco PIX525 配置备忘
·用Iptables+Fedora做ADSL 路由器
·检查 Web 应用安全的几款开源免
·Md5(base64)加密与解密实战
·NT下动态切换进程分析笔记
·风险评估中的渗透测试
  相关文章
·J2EE工程实现中常见安全问题解决
·openrowset/opendatasource的其
·NAT在NDIS中间层驱动中的实现
·必须掌握的安全技能:编译exploi
·NAT在NDIS中间层驱动中的实现
·通用弱点评价体系(CVSS)简介
·MS05-055漏洞分析
·也谈跨站脚本攻击与防御
·Setuid() - nproc limit 类型漏
·从后台得到webshell技巧大汇总
·PHP漏洞中的战争
·一个评价入侵检测系统漏洞攻击检
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved