@@ -384,4 +384,183 @@ download_file("flag", "downloaded_example.txt")
384384
385385## tidy quic
386386
387- 在写了在写了
387+ 这题考的是http3协议
388+
389+ 先分析题目
390+
391+ ```
392+ package main
393+
394+ import (
395+ "bytes"
396+ "errors"
397+ "github.com/libp2p/go-buffer-pool"
398+ "github.com/quic-go/quic-go/http3"
399+ "io"
400+ "log"
401+ "net/http"
402+ )
403+
404+ var p pool.BufferPool
405+ var ErrWAF = errors.New("WAF")
406+
407+ func main() {
408+ go func() {
409+ err := http.ListenAndServeTLS(":8088", "./server.crt", "./server.key", &mux{})
410+ log.Fatalln(err)
411+ }()
412+ go func() {
413+ err := http3.ListenAndServeQUIC(":8088", "./server.crt", "./server.key", &mux{})
414+ log.Fatalln(err)
415+ }()
416+ select {}
417+ }
418+
419+ type mux struct {
420+ }
421+
422+ func (*mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
423+ if r.Method == http.MethodGet {
424+ _, _ = w.Write([]byte("Hello D^3CTF 2025,I'm tidy quic in web."))
425+ return
426+ }
427+ if r.Method != http.MethodPost {
428+ w.WriteHeader(400)
429+ return
430+ }
431+
432+ var buf []byte
433+ length := int(r.ContentLength)
434+ if length == -1 {
435+ var err error
436+ buf, err = io.ReadAll(textInterrupterWrap(r.Body))
437+ if err != nil {
438+ if errors.Is(err, ErrWAF) {
439+ w.WriteHeader(400)
440+ _, _ = w.Write([]byte("WAF"))
441+ } else {
442+ w.WriteHeader(500)
443+ _, _ = w.Write([]byte("error"))
444+ }
445+ return
446+ }
447+ } else {
448+ buf = p.Get(length)
449+ defer p.Put(buf)
450+ rd := textInterrupterWrap(r.Body)
451+ i := 0
452+ for {
453+ n, err := rd.Read(buf[i:])
454+ if err != nil {
455+ if errors.Is(err, io.EOF) {
456+ break
457+ } else if errors.Is(err, ErrWAF) {
458+ w.WriteHeader(400)
459+ _, _ = w.Write([]byte("WAF"))
460+ return
461+ } else {
462+ w.WriteHeader(500)
463+ _, _ = w.Write([]byte("error"))
464+ return
465+ }
466+ }
467+ i += n
468+ }
469+ }
470+ if !bytes.HasPrefix(buf, []byte("I want")) {
471+ _, _ = w.Write([]byte("Sorry I'm not clear what you want."))
472+ return
473+ }
474+ item := bytes.TrimSpace(bytes.TrimPrefix(buf, []byte("I want")))
475+ if bytes.Equal(item, []byte("flag")) {
476+ _, _ = w.Write([]byte("flfag{test}"))
477+ } else {
478+ _, _ = w.Write(item)
479+ }
480+ }
481+
482+ type wrap struct {
483+ io.ReadCloser
484+ ban []byte
485+ idx int
486+ }
487+
488+ func (w *wrap) Read(p []byte) (int, error) {
489+ n, err := w.ReadCloser.Read(p)
490+ if err != nil && !errors.Is(err, io.EOF) {
491+ return n, err
492+ }
493+ for i := 0; i < n; i++ {
494+ if p[i] == w.ban[w.idx] {
495+ w.idx++
496+ if w.idx == len(w.ban) {
497+ return n, ErrWAF
498+ }
499+ } else {
500+ w.idx = 0
501+ }
502+ }
503+ return n, err
504+ }
505+
506+ func textInterrupterWrap(rc io.ReadCloser) io.ReadCloser {
507+ return &wrap{
508+ rc, []byte("flag"), 0,
509+ }
510+ }
511+ ```
512+
513+ 我们在go中的题目主要要关注的点就是在于题目中的全局变量,这差不多是经验之谈了hh
514+
515+ 所以我们可以看到题目中的` var p pool.BufferPool ` ,一个缓冲池
516+
517+ 在http通信中,如果` Content-Length!=-1 ` (即没写CL头),则不会调用缓冲区来读取数据,反之则会使用
518+
519+ ```
520+ buf = p.Get(length)
521+ defer p.Put(buf)
522+ ```
523+
524+ 这里主要在于` defer p.Put(buf) ` ,观察代码上下文也没有对已经写入了body数据的buf缓冲区进行重置清零的操作,而是直接将她放回的缓冲池,这就会导致缓冲池会出现一个被污染的状态,下一次从缓冲池中取出缓冲区也会受到这些数据的影响
525+
526+ 第一次
527+
528+ ![ image-20250602150550525] ( https://tuchuang-1322176132.cos.ap-chengdu.myqcloud.com//imgimage-20250602150550525.png )
529+
530+ 第二次
531+
532+ ![ image-20250602150620439] ( https://tuchuang-1322176132.cos.ap-chengdu.myqcloud.com//imgimage-20250602150620439.png )
533+
534+ 所以我们现在的想法就是,让上次的buf影响到下次的结果
535+
536+ 我们可以让第一次的数据为a bcde flag 第二次的为i want,这样在被污染过后就会成为i want flag,注意这里两次的请求的Content-Length都要为11,即使第二次只post了6个数据,否则取不到对应长度的buf
537+
538+ 接下来的操作体现了http3和http2的区别
539+
540+ http2
541+
542+ 省略了前面post i want flag的一步
543+
544+ ![ image-20250602151342432] ( https://tuchuang-1322176132.cos.ap-chengdu.myqcloud.com//imgimage-20250602151342432.png )
545+
546+ ![ image-20250602151408560] ( https://tuchuang-1322176132.cos.ap-chengdu.myqcloud.com//imgimage-20250602151408560.png )
547+
548+ 结果在读取我们post的10个数据之后,读取行为并没结束,http2还在等待剩下的一位继续输入而并没有发送eof结束
549+
550+ ![ image-20250602151504499] ( https://tuchuang-1322176132.cos.ap-chengdu.myqcloud.com//imgimage-20250602151504499.png )
551+
552+ http3
553+
554+ ![ image-20250602151644729] ( https://tuchuang-1322176132.cos.ap-chengdu.myqcloud.com//imgimage-20250602151644729.png )
555+
556+ 结果http3在一次读取之后就到了eof结束的地方
557+
558+ ![ image-20250602151732504] ( https://tuchuang-1322176132.cos.ap-chengdu.myqcloud.com//imgimage-20250602151732504.png )
559+
560+ ![ image-20250602151855662] ( https://tuchuang-1322176132.cos.ap-chengdu.myqcloud.com//imgimage-20250602151855662.png )
561+
562+
563+
564+ 这里体现出来http2和http3的区别:http2在Content-Length比body实际长度大时,会等待一会儿的输入,来使两者相等,而http3则会更精准的检测出body的实际长度并且在body发送完毕之后迅速的发送结束流,也可以说是quic不会根据http请求包中的Content-Length来界定body的结束
565+
566+ ![ image-20250602152322273] ( https://tuchuang-1322176132.cos.ap-chengdu.myqcloud.com//imgimage-20250602152322273.png )
0 commit comments