Teaser_CONFidence_CTF_2019 The Lottery WP

前置知识

在go语言中存在一个cap(容量),当append的时候回随着长度变成1,2,…,2^n,地址也随之改变,如下,a为[99,99,99]长度为3,a赋值给b时可以发现其地址相同,b在a的基础上加10000,但是由于b长度为3,所以地址不变,则a的第四位被b覆盖

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
package main

import (
"fmt"
)

func main() {
var a []int
a = append(a,99)
fmt.Printf("%p\n",a)
fmt.Println(cap(a))
a = append(a,99)
fmt.Printf("%p\n",a)
fmt.Println(cap(a))
a = append(a,99)
fmt.Printf("%p\n",a)
fmt.Println(cap(a))
b := a
fmt.Printf("%p\n",a)
fmt.Printf("%p\n",b)
a = append(a,99)
b = append(b,10000)
fmt.Println(a)
fmt.Println(b)
}
/*
输出
0xc000014070
1
0xc000088000
2
0xc00008c000
4
0xc00008c000
0xc00008c000
[99 99 99 10000]
[99 99 99 10000]
*/

题目

题目链接
https://pan.baidu.com/s/147EeGUZXx1RxfwAqPyHNWg
提取码 vgpf

分析

看路由

1
2
3
4
5
6
7
// transport/init.go
r.Get("/", handler.IndexGet)
r.Post("/account", handler.AccountAdd)
r.Post("/account/{name}/amount", handler.AccountAddAmount)
r.Get("/account/{name}", handler.AccountGet)
r.Post("/lottery/add", handler.LotteryAdd)
r.Get("/lottery/results", handler.LotteryResults)

取得flag的条件

1
2
3
4
5
6
7
8
9
10
11
// app/service.go
func (s *Service) AccountGet(name string) (Account, bool, error) {
s.mutex.RLock()
defer s.mutex.RUnlock()
account, found := s.accounts[name]
if !found {
return Account{}, false, ErrNotFound
}
superUser := s.lottery.IsWinner(name) || account.IsMillionaire()
return account, superUser, nil
}

一种是amount添加一个随机值后,sum要为0x133700

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// app/lottery.go
func (l *Lottery) evaluate() {
l.mutex.Lock()
defer l.mutex.Unlock()
accounts := l.accounts
l.winners = make(map[string]struct{})
l.accounts = make(map[string]Account)
for name, account := range accounts {
amounts := append(account.Amounts, randInt(999913, 3700000))
sum := 0
for _, a := range amounts {
sum += a
}
if sum == 0x133700 {
l.winners[name] = struct{}{}
}
}
}

一种是sum大于1000000

1
2
3
4
5
6
7
8
// app/account.go
func (a *Account) IsMillionaire() bool {
sum := 0
for _, a := range a.Amounts {
sum += a
}
return sum >= 1000000
}

解题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
s.AccountAddAmount(a.Name, 90)
s.AccountAddAmount(a.Name, 90)
s.AccountAddAmount(a.Name, 90)

// Right now we have a slice with three values [90, 90, 90]
// Do you know what's the capacity and length of it?
// Length=3, Capacity=4.
// It means that after the next 'append' we won't reallocate to the new memory.

// We would like to extend our original slice to length=4 and then wait for
// the lottery's append to overwrite the last item.

// Add account to the 'Lottery'.
s.LotteryAdd(a.Name)
// Immediately after extend 'amounts' slice.
s.AccountAddAmount(a.Name, 90)

// 等待触发
s.lottery.evaluate()