编译和反编译(一)

      编译不是技能,可以交给编译器完成。然而反编译是需要特别的技巧的。其中关键的步骤就是汇编和反汇编。这是连接机器语言和汇编语言的桥梁。注意我这里说的编译是指把高级语言转化为汇编语言,反编译则是把汇编语言转化为高级语言,如C。汇编语言和机器语言之间是一一对应的,所以没有什么技术含量,因此有很多工具能实现这一过程,如等等用到的objdump
      高级语言到汇编语言本来是容易的,但是出于优化目的,这个过程就变得很复杂,编译器承担这一重任,可以说编译器是比操作系统更有内涵的。。在利益的驱动下(人懒),这一过程越来越完美。然而汇编到高级语言的转换却没有那么容易,也没有好的工具。仔细想想为什么。我自己觉得,1. 有些汇编是有扰乱的编码的,没办法转换。2. 很简单的例子,我完全不知寄存器里的是什么数据类型,链表?int?char?数组?机器忠实的执行指令,这些东西完全不关它的事,因此汇编里完全没有必要指定这些数据类型。3. 逻辑,如果有可能完成转换,那必定是充满goto的,因为汇编里只有jmp类型的命令。难以恢复成for,while。然后形成一种科学,叫逆向工程。当然也不是没有可能,比如hex-rays的IDA Pro和Hex-Rays Decompiler,太贵。所以只能手动尝试。

简单汇编:

      Linux和Unix用GAS,Intel却另外搞出一套,不知道为什么。掌握基础的汇编才能看得反汇编后的代码。基本的指令 mov, jmp, cmp, push, pop ,除了知道这些还有了解相应体系结构的寄存器作用和支持的内存寻址方式。大概这书讲得很清楚了。当然学习汇编最好的方式还是跟编译器学。

编译器的汇编:

      具体的我就不说了,我就提一些我遇到的问题。下面是简单例子,取两个数的最小数。

stackframe-cdecl
From http://unixwiz.net/techtips/win32-callconv-asm.html

      非常简单的程序,但是他的内涵是很丰富的。首先看看他的没有经过优化的32位代码。在看懂代码之前,必须了解一下,计算机的stack长什么,或者说是怎么约定的。如上图所示(还有更详细的)。当调用一个函数时,先要传递参数,还要保存之前函数的位置,还有分配现在函数的变量空间。在32位机器上,是先把old ebp 压入stack,然后ebp = esp,彻底进入现有函数。其中ebp+4是返回地址(下一个rip),ebp+8是arg1,ebp+12是arg2,依次类推。我们知道stack是向低位增长的,因此每分配一个局部变量,esp就会减4(相应数据大小)。这个设计,图和代码都表示的很清楚。然后这里有个注意点,就是cmp,在gas里的解释如下:cmp src, dest   :compute dest – src and set flags accordingly。而nasm正好相反。这个flags怎么设置,详见wiki

      然后再来看看经过O1优化的min.c,  gcc -O1 -S min.c -o min.s -m32 -fno-asynchronous-unwind-tables 。结果真是吓尿了,所以gcc的优化还是很必要的。不仅代码规模小了,而且效率高了,直接吊了!但是这里有个问题,看 arg1 变成 4(%esp)了,按照前面来看不是应该 8(%esp)吗。这位仁兄跟我一样的困惑。原因就是这里没有push ebp。之前push ebp时,esp = esp – 4。然后就懂了。

      然后是64位的代码。首先的不同是参数不再使用stack传递,直接使用寄存器,依次是%rdi, %rsi, %rdx, %rcx, %r8 and %r9,(注意是4B类型)然后用stack,详细见abi,链接3。这里有个问题,为什么是-20(%rbp),这里关系到两件事,一是rsp不变,因为red zone的设计。二是stack align,gcc默认对齐到16 bytes。 可以参考手册进行测试。 gcc -S min.c -o min.s -fno-asynchronous-unwind-tables -mpreferred-stack-boundary=4 -momit-leaf-frame-pointer

      最后是64位的优化版本,根本不需要操作内存。看到代码,我只想说这就是进步!

小试身手:

      大概理解了汇编,所以要试着人工反编译。只能借助最原始的gdb和objdump。忽然发现这篇篇幅有点长,因此这里只提个头。Computer Systems: A Programmer’s Perspective非常好的一本书,提供了Lab。第二个实验,破解炸弹,我做的版本不太一样,可以一试,非常有意思。很有成就感。

链接:

  1.  反编译工具
  2. Reengineering from Assembler to C via FermaT Transformations
  3. Hex-Rays
  4. x86-64 API,   Stack frame layout on x86-64Intel x86 Function-call Conventions, ret
  5. simple tutorial
  6. optimization assembly
  7. Calling Conventions, Red zone, Stack align

编译和反编译(二)

      上文说了汇编语言的基本要素和一些体系结构的东西。现在就付诸实践,来反编译试试。我用的是从一个公开课The Hardware/Software Interface上的lab,我上传了github。有兴趣的可以自己尝试。我花了大概5个小时才完全破解。而且还有一个过程没有弄明白,看到结果才明白过程是什么。。

GDB基础:

      具体的可以看链接1。我基本就用到了下面的选项。i r和x是特别有用的。

objdump:

      首先还是先把所有代码反汇编了再说,否则根本不知道框架。 objdump -d bomb > bomb.s 搞定。这下可以看出一共7个阶段,包括一个secret阶段。然后试着用 strings bomb ,说不定有惊喜。这样心里就更有数了,因为出来有一部分是这样的结果:

That’s number 2. Keep going!
Halfway there!
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
So you got that one. Try this one.
Congratulations! You’ve (mostly) defused the bomb!
Hit Control-C to escape phase 6 (for free!), but if you want to
try phase 6 for extra credit, you can continue. Just beware!
Science isn’t about why, it’s about why not?
Wow! You’ve defused the secret stage!
So you think you can stop the bomb with ctrl-c, do you?
nonexistenthost.cs.washington.edu
Curses, you’ve found the secret phase!
But finding it and solving it are quite different…
Congratulations! You’ve defused the bomb! Again!

正式破解:

阶段一:
      这一步很简单,以下4个命令就能破解。你懂的,$esi就是参数。

阶段二:
      从objdump的bomb.s可以看出,这次要输入6个数字。通过下面3步可以知道我输入的数字存储的位置。我刚开始当然不知道答案是哪几个数字,所以随便放几个。

从这里就可以看出这是要测试第零个数字和第三个是否一样,同理看看其他是什么关系。

阶段三:
      这个好多mov, jmp。看到就吓尿了。关键点是 400f2b: ff 24 c5 60 1b 40 00 jmpq *0x401b60(,%rax,8)  ,我刚开始不明白其意义,是根据答案得到结果的。后来经查找,发现这是switch,用 x /20xw 0x401b60 就可以检查。然后迎刃而解。

阶段四:
      不说了,是个fibonacci数列。我觉得是第二简单的一个。

阶段五:
      首先这里有个数组,如下: 然后程序的目的是经过12次指针切换后,最后指到值为15的array[i]。可知上一次i = 6。依次类推。然后他也是输入两个数字,一个是起点,一个是总和。可以看出每次迭代都是把值相加,跟输入值相比。

阶段六: 
      那个fun6完全没有看懂,但是可以进行下去。按下面的步骤,得出的结果,一定能发现什么。然后就简单了。

Secret Phase:
      在这里我搞了近两个小时。首先是找不到入口。phase_defused中明白的写着是从这进,而且要输入两个参数。而且第二个字符串是austinpowers。但是半天没有搞定。最后发现居然不是在最后输入。。。在哪输入自己找。顺利进入secret phase。

      进入secret phase后,查看相关数据。发现也是链表类型的。然后进入fun7。这是个递归的函数。不用笔不行啊,然后我就手动写了递归函数。坑的是我把cmp搞反了,郁闷了半天。 c代码都写出来了就简单了。

链接:

  1. GDB tutorial

Metasploit第一次渗透记录(后渗透阶段)

      我去TM的,网络课程作业终于写完,遇上坑爹老师也真蛋疼。
      上次干了什么事都快忘记了,再不写就细节就没了。上次说到上传了php文件当shell。我可以在那个文件下执行系统命令。为了碰碰运气,可以输入id看看目前是以什么身份登录的。一般都是 uid=33(www-data) gid=33(www-data) groups=33(www-data) 。所以现在权限不大,要找其他弱点。可以先用 uname -a, df -h, ifconfig -a, ps aux 等等命令做个小检查,了解一下系统的确切信息。

目录浏览:

       cd, ls -lart  两个命令就可以对目标系统做个大体的检查,一不小心就能看到权限不对的文件或者文件目录。当然也可以用find命令自动搜索权限不对的文件,总感觉不太有趣。

      首先我看到了/var/www目录里有wordpress,权限为777,当时就哭了。(因为我觉得这个shell太简单,用起来不方便,所以我想上传更强大的shell,在joomla!这个网站下,滤过了不安全的文件,就算我用base64编码后,成功上传后也无法运行。)而777权限的wordpress的出现就像救星一样,立马把之前的大shell移动过来。看现在的shell。作者是Zaco。如果我邪恶一点,rm -rf,那就坑爹了。不仅如此,那系统里还有很多755的,所以我能读取很多信息。就不说是哪些信息了。

shell2

登录后台:

      因为有网站目录的读权限,而一般数据库密码都是明文保存在相关目录下,比如config.php啊什么的,很容易就能找到数据库密码。根据恢复密码方法,立刻拿下joomla!后台admin。结果也没什么意思。不是什么大网站,基本没有用户信息。直接放弃。

用户分析:

      光是www-data可不行啊,必须有其他用户权限才行。基于很多人都会记下一些密码啊什么的,我首先分析了其中最有可能有sudo权限的人,同样是用ls, cat等等。结果一筹莫展,所以还是另辟他径。网上一搜,linux privilege escalation, 找到了CVE-2012-0056,我去运气太好。立马上传,然后编译运行。发现没有出现应该出现的情况。但是通过ps aux发现确实由我产生了root进程,只是不能输入命令。难道debian特别吊?只能放弃。

      在半夜12点,就要收拾去睡觉的时候奇思妙想,不如来猜一猜密码。然后一猜就中,6个用户里有两个用户的密码就是用户名。可惜是摆设用户,没有sudo权限。但是由此获得了SSH登录,操作就方便多了。然后就去睡觉了。

homeuser

ROOT权限:

      没有获取root权限总觉得不爽。。因此又回到了原点。根据这里的引导,我一步步测试,结果还是没有任何进展。随手来了句 netstat -an | egrep 'Proto|LISTEN' 忽然发现有个ftp端口。发现是pure-ftpd,上网一搜,知道其配置文件的位置。cd过去,然后我就哭了。。有个文件是可读的,打开一看,里面有个密码,我擦,就是默认的数据库密码。然后直接命令行进入数据库。虽然密码是加密的,但是只有把MD5放到google,就能得到原文。里面有个用户名就是系统的用户,使用其密码登录,果然行。但是去他妈的,这个吊用户也不在sudoers里,我算是明白了,看了他的.bash_history,tmd他们都是直接su -的。所以我最后还是没有root权限。但是获得了那个wordpress的后台,原因是密码是一样的。

dbpass

Metasploit Shell:

      之前这些步骤都是手动完成的,跟metasploit基本没有关系。我在想,如果我手动得到php shell之后怎么跟metasploit连接。目前找到的解决方案是手动编码一个python reverse shell, 参考此处。然后跟metasploit连接就行了,操作起来就像普通shell。

参考:

  1. linux privilege escalationMempodipper
  2. python Meterpreter Reverse Shell
  3. php reverse shell